Ignore null character when waiting for autoboot abort
[oweals/u-boot_mod.git] / u-boot / common / main.c
1 /*
2  * (C) Copyright 2000
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
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.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23
24 /* #define      DEBUG   */
25
26 #include <common.h>
27 #include <command.h>
28
29 #ifdef CFG_HUSH_PARSER
30 #include <hush.h>
31 #endif
32
33 #ifdef CONFIG_SILENT_CONSOLE
34 DECLARE_GLOBAL_DATA_PTR;
35 #endif
36
37 extern int reset_button_status(void);
38 extern void all_led_on(void);
39 extern void all_led_off(void);
40 extern int NetLoopHttpd(void);
41
42 #define MAX_DELAY_STOP_STR 32
43
44 static char *delete_char(char *buffer, char *p, int *colp, int *np, int plen);
45 static int parse_line(char *, char *[]);
46 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
47 static int abortboot(int);
48 #endif
49
50 char console_buffer[CFG_CBSIZE]; /* console I/O buffer  */
51 static char erase_seq[] = "\b \b"; /* erase sequence    */
52 static char tab_seq[] = "        "; /* used to expand TABs      */
53
54 /***************************************************************************
55  * Watch for 'delay' seconds for autoboot stop or autoboot delay string.
56  * returns: 0 -  no key string, allow autoboot
57  *          1 - got key string, abort
58  */
59 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
60 static __inline__ int abortboot(int bootdelay){
61         char stopc;
62         int abort = 0;
63
64 #ifdef CONFIG_SILENT_CONSOLE
65         if(gd->flags & GD_FLG_SILENT){
66                 /* Restore serial console */
67                 console_assign(stdout, "serial");
68                 console_assign(stderr, "serial");
69         }
70 #endif
71
72         if(bootdelay > 0){
73 #ifdef CONFIG_MENUPROMPT
74                 printf(CONFIG_MENUPROMPT, bootdelay);
75 #else
76                 printf("Hit any key to stop autoboot: %d ", bootdelay);
77 #endif
78
79                 while((bootdelay > 0) && (!abort)){
80                         int i;
81
82                         --bootdelay;
83
84                         /* delay 100 * 10ms */
85                         for(i = 0; !abort && i < 100; ++i){
86                                 /* we got a key press   */
87                                 if(tstc()){
88                                         stopc = getc();
89 #ifdef CONFIG_AUTOBOOT_STOP_CHAR
90                                         if (stopc == CONFIG_AUTOBOOT_STOP_CHAR) {
91 #else
92                                         if (stopc != 0) {
93 #endif
94                                                 abort = 1;
95                                                 bootdelay = 0;
96
97                                                 break;
98                                         }
99                                 }
100                                 udelay(10000);
101                         }
102
103                         printf("\b\b%d ", bootdelay);
104                 }
105
106                 printf("\n\n");
107         }
108
109 #ifdef CONFIG_SILENT_CONSOLE
110         if(abort){
111                 /* permanently enable normal console output */
112                 gd->flags &= ~(GD_FLG_SILENT);
113         } else if(gd->flags & GD_FLG_SILENT){
114                 /* Restore silent console */
115                 console_assign(stdout, "nulldev");
116                 console_assign(stderr, "nulldev");
117         }
118 #endif
119
120         return(abort);
121 }
122 #endif  /* CONFIG_BOOTDELAY >= 0  */
123
124 /****************************************************************************/
125
126 void main_loop(void){
127 #ifndef CFG_HUSH_PARSER
128         static char lastcommand[CFG_CBSIZE] = { 0, };
129         int len;
130         int rc = 1;
131         int flag;
132 #endif
133         int counter = 0;
134
135 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
136         char *s;
137         int bootdelay;
138 #endif /* defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) */
139
140 #ifdef CFG_HUSH_PARSER
141         u_boot_hush_start();
142 #endif
143
144 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
145         // get boot delay (seconds)
146         s = getenv("bootdelay");
147         bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
148
149         // get boot command
150         s = getenv("bootcmd");
151
152 #if !defined(CONFIG_BOOTCOMMAND)
153 #error "CONFIG_BOOTCOMMAND not defined!"
154 #endif
155
156         if(!s){
157                 setenv("bootcmd", CONFIG_BOOTCOMMAND);
158         }
159
160         s = getenv("bootcmd");
161
162         // are we going to run web failsafe mode, U-Boot console, U-Boot netconsole or just boot command?
163         if(reset_button_status()){
164
165 #ifdef CONFIG_SILENT_CONSOLE
166                 if(gd->flags & GD_FLG_SILENT){
167                         /* Restore serial console */
168                         console_assign(stdout, "serial");
169                         console_assign(stderr, "serial");
170                 }
171
172                 /* enable normal console output */
173                 gd->flags &= ~(GD_FLG_SILENT);
174 #endif
175
176                 // wait 0,5s
177                 milisecdelay(500);
178
179                 printf("Press reset button for at least:\n- %d sec. to run web failsafe mode\n- %d sec. to run U-Boot console\n- %d sec. to run U-Boot netconsole\n\n",
180                                 CONFIG_DELAY_TO_AUTORUN_HTTPD,
181                                 CONFIG_DELAY_TO_AUTORUN_CONSOLE,
182                                 CONFIG_DELAY_TO_AUTORUN_NETCONSOLE);
183
184                 printf("Reset button is pressed for: %2d ", counter);
185
186                 while(reset_button_status()){
187
188                         // LED ON and wait 0,15s
189                         all_led_on();
190                         milisecdelay(150);
191
192                         // LED OFF and wait 0,85s
193                         all_led_off();
194                         milisecdelay(850);
195
196                         counter++;
197
198                         // how long the button is pressed?
199                         printf("\b\b\b%2d ", counter);
200
201                         if(!reset_button_status()){
202                                 break;
203                         }
204
205                         if(counter >= CONFIG_MAX_BUTTON_PRESSING){
206                                 break;
207                         }
208                 }
209
210                 all_led_off();
211
212                 if(counter > 0){
213
214                         // run web failsafe mode
215                         if(counter >= CONFIG_DELAY_TO_AUTORUN_HTTPD && counter < CONFIG_DELAY_TO_AUTORUN_CONSOLE){
216                                 printf("\n\nButton was pressed for %d sec...\nHTTP server is starting for firmware update...\n\n", counter);
217                                 NetLoopHttpd();
218                                 bootdelay = -1;
219                         } else if(counter >= CONFIG_DELAY_TO_AUTORUN_CONSOLE && counter < CONFIG_DELAY_TO_AUTORUN_NETCONSOLE){
220                                 printf("\n\nButton was pressed for %d sec...\nStarting U-Boot console...\n\n", counter);
221                                 bootdelay = -1;
222                         } else if(counter >= CONFIG_DELAY_TO_AUTORUN_NETCONSOLE){
223                                 printf("\n\nButton was pressed for %d sec...\nStarting U-Boot netconsole...\n\n", counter);
224                                 bootdelay = -1;
225                                 run_command("startnc", 0);
226                         } else {
227                                 printf("\n\n## Error: button wasn't pressed long enough!\nContinuing normal boot...\n\n");
228                         }
229
230                 } else {
231                         printf("\n\n## Error: button wasn't pressed long enough!\nContinuing normal boot...\n\n");
232                 }
233
234         }
235
236         if(bootdelay >= 0 && s && !abortboot(bootdelay)){
237
238                 // try to boot
239 #ifndef CFG_HUSH_PARSER
240                         run_command(s, 0);
241 #else
242                         parse_string_outer(s, FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP);
243 #endif
244
245                 // something goes wrong!
246                 printf("\n## Error: failed to execute 'bootcmd'!\nHTTP server is starting for firmware update...\n\n");
247                 NetLoopHttpd();
248         }
249 #endif  /* CONFIG_BOOTDELAY */
250
251         /*
252          * Main Loop for Monitor Command Processing
253          */
254 #ifdef CFG_HUSH_PARSER
255         parse_file_outer();
256         /* This point is never reached */
257         for (;;);
258 #else
259         for(;;){
260                 len = readline(CFG_PROMPT);
261
262                 flag = 0; /* assume no special flags for now */
263                 if(len > 0){
264                         strcpy(lastcommand, console_buffer);
265                 } else if(len == 0){
266                         flag |= CMD_FLAG_REPEAT;
267                 }
268
269                 if(len == -1){
270                         puts("<INTERRUPT>\n");
271                 } else {
272                         rc = run_command(lastcommand, flag);
273                 }
274
275                 if(rc <= 0){
276                         /* invalid command or not repeatable, forget it */
277                         lastcommand[0] = 0;
278                 }
279         }
280 #endif /* CFG_HUSH_PARSER */
281 }
282
283 /****************************************************************************/
284
285 /*
286  * Prompt for input and read a line.
287  * If  CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0,
288  * time out when time goes past endtime (timebase time in ticks).
289  * Return:      number of read characters
290  *              -1 if break
291  *              -2 if timed out
292  */
293 int readline(const char * const prompt){
294         char *p = console_buffer;
295         int n = 0; /* buffer index              */
296         int plen = 0; /* prompt length  */
297         int col; /* output column cnt   */
298         char c;
299
300         /* print prompt */
301         if(prompt){
302                 plen = strlen(prompt);
303                 puts(prompt);
304         }
305         col = plen;
306
307         for(;;){
308                 c = getc();
309
310                 /*
311                  * Special character handling
312                  */
313                 switch(c){
314                         case '\r': /* Enter             */
315                         case '\n':
316                                 *p = '\0';
317                                 puts("\r\n");
318                                 return(p - console_buffer);
319
320                         case '\0': /* nul                       */
321                                 continue;
322
323                         case 0x03: /* ^C - break                */
324                                 console_buffer[0] = '\0'; /* discard input */
325                                 return(-1);
326
327                         case 0x15: /* ^U - erase line   */
328                                 while(col > plen){
329                                         puts(erase_seq);
330                                         --col;
331                                 }
332                                 p = console_buffer;
333                                 n = 0;
334                                 continue;
335
336                         case 0x17: /* ^W - erase word   */
337                                 p = delete_char(console_buffer, p, &col, &n, plen);
338                                 while((n > 0) && (*p != ' ')){
339                                         p = delete_char(console_buffer, p, &col, &n, plen);
340                                 }
341                                 continue;
342
343                         case 0x08: /* ^H  - backspace   */
344                         case 0x7F: /* DEL - backspace   */
345                                 p = delete_char(console_buffer, p, &col, &n, plen);
346                                 continue;
347
348                         default:
349                                 /*
350                                  * Must be a normal character then
351                                  */
352                                 if(n < CFG_CBSIZE - 2){
353                                         if(c == '\t'){ /* expand TABs           */
354                                                 puts(tab_seq + (col & 07));
355                                                 col += 8 - (col & 07);
356                                         } else {
357                                                 ++col; /* echo input            */
358                                                 putc(c);
359                                         }
360                                         *p++ = c;
361                                         ++n;
362                                 } else { /* Buffer full         */
363                                         putc('\a');
364                                 }
365                 }
366         }
367 }
368
369 /****************************************************************************/
370
371 static char * delete_char(char *buffer, char *p, int *colp, int *np, int plen){
372         char *s;
373
374         if(*np == 0){
375                 return(p);
376         }
377
378         if(*(--p) == '\t'){ /* will retype the whole line       */
379                 while(*colp > plen){
380                         puts(erase_seq);
381                         (*colp)--;
382                 }
383                 for(s = buffer; s < p; ++s){
384                         if(*s == '\t'){
385                                 puts(tab_seq + ((*colp) & 07));
386                                 *colp += 8 - ((*colp) & 07);
387                         } else {
388                                 ++(*colp);
389                                 putc(*s);
390                         }
391                 }
392         } else {
393                 puts(erase_seq);
394                 (*colp)--;
395         }
396         (*np)--;
397         return(p);
398 }
399
400 /****************************************************************************/
401
402 int parse_line(char *line, char *argv[]){
403         int nargs = 0;
404
405         while(nargs < CFG_MAXARGS){
406
407                 /* skip any white space */
408                 while((*line == ' ') || (*line == '\t')){
409                         ++line;
410                 }
411
412                 if(*line == '\0'){ /* end of line, no more args */
413                         argv[nargs] = NULL;
414                         return(nargs);
415                 }
416
417                 argv[nargs++] = line; /* begin of argument string       */
418
419                 /* find end of string */
420                 while(*line && (*line != ' ') && (*line != '\t')){
421                         ++line;
422                 }
423
424                 if(*line == '\0'){ /* end of line, no more args */
425                         argv[nargs] = NULL;
426                         return(nargs);
427                 }
428
429                 *line++ = '\0'; /* terminate current arg         */
430         }
431
432         printf("## Error: too many args (max. %d)\n", CFG_MAXARGS);
433
434         return(nargs);
435 }
436
437 /****************************************************************************/
438
439 static void process_macros(const char *input, char *output){
440         char c, prev;
441         const char *varname_start = NULL;
442         int inputcnt = strlen(input);
443         int outputcnt = CFG_CBSIZE;
444         int state = 0; /* 0 = waiting for '$'   */
445         /* 1 = waiting for '(' or '{' */
446         /* 2 = waiting for ')' or '}' */
447         /* 3 = waiting for '''  */
448
449         prev = '\0'; /* previous character      */
450
451         while(inputcnt && outputcnt){
452                 c = *input++;
453                 inputcnt--;
454
455                 if(state != 3){
456                         /* remove one level of escape characters */
457                         if((c == '\\') && (prev != '\\')){
458                                 if(inputcnt-- == 0){
459                                         break;
460                                 }
461
462                                 prev = c;
463                                 c = *input++;
464                         }
465                 }
466
467                 switch(state){
468                         case 0: /* Waiting for (unescaped) $    */
469                                 if((c == '\'') && (prev != '\\')){
470                                         state = 3;
471                                         break;
472                                 }
473                                 if((c == '$') && (prev != '\\')){
474                                         state++;
475                                 } else {
476                                         *(output++) = c;
477                                         outputcnt--;
478                                 }
479                                 break;
480                         case 1: /* Waiting for (        */
481                                 if(c == '(' || c == '{'){
482                                         state++;
483                                         varname_start = input;
484                                 } else {
485                                         state = 0;
486                                         *(output++) = '$';
487                                         outputcnt--;
488
489                                         if(outputcnt){
490                                                 *(output++) = c;
491                                                 outputcnt--;
492                                         }
493                                 }
494                                 break;
495                         case 2: /* Waiting for )        */
496                                 if(c == ')' || c == '}'){
497                                         int i;
498                                         char envname[CFG_CBSIZE], *envval;
499                                         int envcnt = input - varname_start - 1; /* Varname # of chars */
500
501                                         /* Get the varname */
502                                         for(i = 0; i < envcnt; i++){
503                                                 envname[i] = varname_start[i];
504                                         }
505                                         envname[i] = 0;
506
507                                         /* Get its value */
508                                         envval = getenv(envname);
509
510                                         /* Copy into the line if it exists */
511                                         if(envval != NULL){
512                                                 while((*envval) && outputcnt){
513                                                         *(output++) = *(envval++);
514                                                         outputcnt--;
515                                                 }
516                                         }
517                                         /* Look for another '$' */
518                                         state = 0;
519                                 }
520                                 break;
521                         case 3: /* Waiting for '        */
522                                 if((c == '\'') && (prev != '\\')){
523                                         state = 0;
524                                 } else {
525                                         *(output++) = c;
526                                         outputcnt--;
527                                 }
528                                 break;
529                 }
530                 prev = c;
531         }
532
533         if(outputcnt){
534                 *output = 0;
535         }
536 }
537
538 /****************************************************************************
539  * returns:
540  *      1  - command executed, repeatable
541  *      0  - command executed but not repeatable, interrupted commands are
542  *           always considered not repeatable
543  *      -1 - not executed (unrecognized, bootd recursion or too many args)
544  *           (If cmd is NULL or "" or longer than CFG_CBSIZE-1 it is
545  *           considered unrecognized)
546  *
547  * WARNING:
548  *
549  * We must create a temporary copy of the command since the command we get
550  * may be the result from getenv(), which returns a pointer directly to
551  * the environment data, which may change magicly when the command we run
552  * creates or modifies environment variables (like "bootp" does).
553  */
554
555 int run_command(const char *cmd, int flag){
556         cmd_tbl_t *cmdtp;
557         char cmdbuf[CFG_CBSIZE]; /* working copy of cmd         */
558         char *token; /* start of token in cmdbuf        */
559         char *sep; /* end of token (separator) in cmdbuf */
560         char finaltoken[CFG_CBSIZE];
561         char *str = cmdbuf;
562         char *argv[CFG_MAXARGS + 1]; /* NULL terminated */
563         int argc, inquotes;
564         int repeatable = 1;
565         int rc = 0;
566
567         clear_ctrlc(); /* forget any previous Control C */
568
569         if(!cmd || !*cmd){
570                 return(-1); /* empty command */
571         }
572
573         if(strlen(cmd) >= CFG_CBSIZE){
574                 puts("## Error: command too long!\n");
575                 return(-1);
576         }
577
578         strcpy(cmdbuf, cmd);
579
580         /* Process separators and check for invalid
581          * repeatable commands
582          */
583         while(*str){
584
585                 /*
586                  * Find separator, or string end
587                  * Allow simple escape of ';' by writing "\;"
588                  */
589                 for(inquotes = 0, sep = str; *sep; sep++){
590                         if((*sep == '\'') && (*(sep - 1) != '\\')){
591                                 inquotes = !inquotes;
592                         }
593
594                         if(!inquotes && (*sep == ';') && (sep != str) && (*(sep - 1) != '\\')){
595                                 break;
596                         }
597                 }
598
599                 /*
600                  * Limit the token to data between separators
601                  */
602                 token = str;
603                 if(*sep){
604                         str = sep + 1; /* start of command for next pass */
605                         *sep = '\0';
606                 } else {
607                         str = sep; /* no more commands for next pass */
608                 }
609
610                 /* find macros in this token and replace them */
611                 process_macros(token, finaltoken);
612
613                 /* Extract arguments */
614                 if((argc = parse_line(finaltoken, argv)) == 0){
615                         rc = -1; /* no command at all */
616                         continue;
617                 }
618
619                 /* Look up command in command table */
620                 if((cmdtp = find_cmd(argv[0])) == NULL){
621                         printf("## Error: unknown command '%s' - try 'help'\n\n", argv[0]);
622                         rc = -1; /* give up after bad command */
623                         continue;
624                 }
625
626                 /* found - check max args */
627                 if(argc > cmdtp->maxargs){
628 #ifdef CFG_LONGHELP
629                         if(cmdtp->help != NULL){
630                                 printf("Usage:\n%s %s\n", cmdtp->name, cmdtp->help);
631                         } else {
632                                 printf("Usage:\n%s %s\n", cmdtp->name, cmdtp->usage);
633                         }
634 #else
635                         printf("Usage:\n%s %s\n", cmdtp->name, cmdtp->usage);
636 #endif
637                         rc = -1;
638                         continue;
639                 }
640
641                 /* OK - call function to do the command */
642                 if((cmdtp->cmd)(cmdtp, flag, argc, argv) != 0){
643                         rc = -1;
644                 }
645
646                 repeatable &= cmdtp->repeatable;
647
648                 /* Did the user stop this? */
649                 if(had_ctrlc()){ /* if stopped then not repeatable */
650                         return(0);
651                 }
652         }
653
654         return(rc ? rc : repeatable);
655 }
656
657 /****************************************************************************/
658
659 #if (CONFIG_COMMANDS & CFG_CMD_RUN)
660 int do_run(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]){
661         int i;
662
663         if(argc < 2){
664 #ifdef CFG_LONGHELP
665                 if(cmdtp->help != NULL){
666                         printf("Usage:\n%s %s\n", cmdtp->name, cmdtp->help);
667                 } else {
668                         printf("Usage:\n%s %s\n", cmdtp->name, cmdtp->usage);
669                 }
670 #else
671                 printf("Usage:\n%s %s\n", cmdtp->name, cmdtp->usage);
672 #endif
673                 return(1);
674         }
675
676         for(i=1; i<argc; ++i){
677                 char *arg;
678
679                 if((arg = getenv(argv[i])) == NULL){
680                         printf("## Error: \"%s\" not defined\n", argv[i]);
681                         return(1);
682                 }
683 #ifndef CFG_HUSH_PARSER
684                 if(run_command(arg, flag) == -1){
685                         return(1);
686                 }
687 #else
688                 if (parse_string_outer(arg, FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP) != 0){
689                         return(1);
690                 }
691 #endif /* CFG_HUSH_PARSER */
692         }
693
694         return(0);
695 }
696 #endif  /* CFG_CMD_RUN */