diff options
Diffstat (limited to 'common/main.c')
| -rw-r--r-- | common/main.c | 817 | 
1 files changed, 817 insertions, 0 deletions
| diff --git a/common/main.c b/common/main.c new file mode 100644 index 000000000..014804b6e --- /dev/null +++ b/common/main.c @@ -0,0 +1,817 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <watchdog.h> +#include <command.h> +#include <cmd_nvedit.h> +#include <cmd_bootm.h> +#include <malloc.h> +#if defined(CONFIG_BOOT_RETRY_TIME) && defined(CONFIG_RESET_TO_RETRY) +#include <cmd_boot.h>		/* for do_reset() prototype */ +#endif + +#ifdef CFG_HUSH_PARSER +#include <hush.h> +#endif + +#define MAX_DELAY_STOP_STR 32 + +static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen); +static int parse_line (char *, char *[]); +#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) +static int abortboot(int); +#endif + +#undef DEBUG_PARSER + +char        console_buffer[CFG_CBSIZE];		/* console I/O buffer	*/ + +static char erase_seq[] = "\b \b";		/* erase sequence	*/ +static char   tab_seq[] = "        ";		/* used to expand TABs	*/ + +#ifdef CONFIG_BOOT_RETRY_TIME +static uint64_t endtime = 0;  /* must be set, default is instant timeout */ +static int      retry_time = -1; /* -1 so can call readline before main_loop */ +#endif + +#define	endtick(seconds) (get_ticks() + (uint64_t)(seconds) * get_tbclk()) + +#ifndef CONFIG_BOOT_RETRY_MIN +#define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME +#endif + +#ifdef CONFIG_MODEM_SUPPORT +int do_mdm_init = 0; +extern void mdm_init(void); /* defined in board.c */ +#endif + +/*************************************************************************** + * Watch for 'delay' seconds for autoboot stop or autoboot delay string. + * returns: 0 -  no key string, allow autoboot + *          1 - got key string, abort + */ +#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) +# if defined(CONFIG_AUTOBOOT_KEYED) +static __inline__ int abortboot(int bootdelay) +{ +	int abort = 0; +	uint64_t etime = endtick(bootdelay); +	struct +	{ +		char* str; +		u_int len; +		int retry; +	} +	delaykey [] = +	{ +		{ str: getenv ("bootdelaykey"),  retry: 1 }, +		{ str: getenv ("bootdelaykey2"), retry: 1 }, +		{ str: getenv ("bootstopkey"),   retry: 0 }, +		{ str: getenv ("bootstopkey2"),  retry: 0 }, +	}; + +	char presskey [MAX_DELAY_STOP_STR]; +	u_int presskey_len = 0; +	u_int presskey_max = 0; +	u_int i; + +#  ifdef CONFIG_AUTOBOOT_PROMPT +	printf (CONFIG_AUTOBOOT_PROMPT, bootdelay); +#  endif + +#  ifdef CONFIG_AUTOBOOT_DELAY_STR +	if (delaykey[0].str == NULL) +		delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR; +#  endif +#  ifdef CONFIG_AUTOBOOT_DELAY_STR2 +	if (delaykey[1].str == NULL) +		delaykey[1].str = CONFIG_AUTOBOOT_DELAY_STR2; +#  endif +#  ifdef CONFIG_AUTOBOOT_STOP_STR +	if (delaykey[2].str == NULL) +		delaykey[2].str = CONFIG_AUTOBOOT_STOP_STR; +#  endif +#  ifdef CONFIG_AUTOBOOT_STOP_STR2 +	if (delaykey[3].str == NULL) +		delaykey[3].str = CONFIG_AUTOBOOT_STOP_STR2; +#  endif + +	for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) { +		delaykey[i].len = delaykey[i].str == NULL ? +				    0 : strlen (delaykey[i].str); +		delaykey[i].len = delaykey[i].len > MAX_DELAY_STOP_STR ? +				    MAX_DELAY_STOP_STR : delaykey[i].len; + +		presskey_max = presskey_max > delaykey[i].len ? +				    presskey_max : delaykey[i].len; + +#  if DEBUG_BOOTKEYS +		printf("%s key:<%s>\n", +		       delaykey[i].retry ? "delay" : "stop", +		       delaykey[i].str ? delaykey[i].str : "NULL"); +#  endif +	} + +	/* In order to keep up with incoming data, check timeout only +	 * when catch up. +	 */ +	while (!abort && get_ticks() <= etime) { +		for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) { +			if (delaykey[i].len > 0 && +			    presskey_len >= delaykey[i].len && +			    memcmp (presskey + presskey_len - delaykey[i].len, +		        	    delaykey[i].str, +				    delaykey[i].len) == 0) { +#  if DEBUG_BOOTKEYS +				printf("got %skey\n", +				       delaykey[i].retry ? "delay" : "stop"); +#  endif + +#  ifdef CONFIG_BOOT_RETRY_TIME +				/* don't retry auto boot */ +				if (! delaykey[i].retry) +					retry_time = -1; +#  endif +				abort = 1; +			} +		} + +		if (tstc()) { +			if (presskey_len < presskey_max) { +				presskey [presskey_len ++] = getc(); +			} +			else { +				for (i = 0; i < presskey_max - 1; i ++) +					presskey [i] = presskey [i + 1]; + +				presskey [i] = getc(); +			} +		} +	} +#  if DEBUG_BOOTKEYS +	if (!abort) +		printf("key timeout\n"); +#  endif + +	return abort; +} + +# else	/* !defined(CONFIG_AUTOBOOT_KEYED) */ + +static __inline__ int abortboot(int bootdelay) +{ +	int abort = 0; + +	printf("Hit any key to stop autoboot: %2d ", bootdelay); + +#if defined CONFIG_ZERO_BOOTDELAY_CHECK +        /* +         * Check if key already pressed +         * Don't check if bootdelay < 0 +         */ +	if (bootdelay >= 0) { +		if (tstc()) {	/* we got a key press	*/ +			(void) getc();  /* consume input	*/ +			printf ("\b\b\b 0\n"); +			return 1; 	/* don't auto boot	*/ +		} +        } +#endif + +	while (bootdelay > 0) { +		int i; + +		--bootdelay; +		/* delay 100 * 10ms */ +		for (i=0; !abort && i<100; ++i) { +			if (tstc()) {	/* we got a key press	*/ +				abort  = 1;	/* don't auto boot	*/ +				bootdelay = 0;	/* no more delay	*/ +				(void) getc();  /* consume input	*/ +				break; +			} +			udelay (10000); +		} + +		printf ("\b\b\b%2d ", bootdelay); +	} + +	putc ('\n'); + +	return abort; +} +# endif	/* CONFIG_AUTOBOOT_KEYED */ +#endif	/* CONFIG_BOOTDELAY >= 0  */ + +/****************************************************************************/ + +void main_loop (void) +{ +#ifndef CFG_HUSH_PARSER +	static char lastcommand[CFG_CBSIZE] = { 0, }; +	int len; +	int rc = 1; +	int flag; +#endif + +#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) +	char *s; +	int bootdelay; +#endif +#ifdef CONFIG_PREBOOT +	char *p; +#endif + +#if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO) +	ulong bmp = 0;		/* default bitmap */ +	extern int trab_vfd (ulong bitmap); + +#ifdef CONFIG_MODEM_SUPPORT +	if (do_mdm_init) +		bmp = 1;	/* alternate bitmap */ +#endif +	trab_vfd (bmp); +#endif	/* CONFIG_VFD && VFD_TEST_LOGO */ + +#ifdef CONFIG_MODEM_SUPPORT +	debug ("DEBUG: main_loop:   do_mdm_init=%d\n", do_mdm_init); +	if (do_mdm_init) { +		uchar *str = strdup(getenv("mdm_cmd")); +		setenv ("preboot", str);  /* set or delete definition */ +		if (str != NULL) +			free (str); +		mdm_init(); /* wait for modem connection */ +	} +#endif  /* CONFIG_MODEM_SUPPORT */ + +#ifdef CFG_HUSH_PARSER +	u_boot_hush_start (); +#endif + +#ifdef CONFIG_PREBOOT +	if ((p = getenv ("preboot")) != NULL) { +# ifdef CONFIG_AUTOBOOT_KEYED +		int prev = disable_ctrlc(1);	/* disable Control C checking */ +# endif + +# ifndef CFG_HUSH_PARSER +		run_command (p, 0); +# else +		parse_string_outer(p, FLAG_PARSE_SEMICOLON | +				    FLAG_EXIT_FROM_LOOP); +# endif + +# ifdef CONFIG_AUTOBOOT_KEYED +		disable_ctrlc(prev);	/* restore Control C checking */ +# endif +	} +#endif /* CONFIG_PREBOOT */ + +#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) +	s = getenv ("bootdelay"); +	bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; + +#if 0 +	printf ("### main_loop entered:\n\n"); +#endif + +# ifdef CONFIG_BOOT_RETRY_TIME +	s = getenv ("bootretry"); +	if (s != NULL) +		retry_time = (int)simple_strtoul(s, NULL, 10); +	else +		retry_time =  CONFIG_BOOT_RETRY_TIME; +	if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN) +		retry_time = CONFIG_BOOT_RETRY_MIN; +# endif	/* CONFIG_BOOT_RETRY_TIME */ + +	s = getenv ("bootcmd"); +	if (bootdelay >= 0 && s && !abortboot (bootdelay)) { +# ifdef CONFIG_AUTOBOOT_KEYED +		int prev = disable_ctrlc(1);	/* disable Control C checking */ +# endif + +# ifndef CFG_HUSH_PARSER +		run_command (s, 0); +# else +		parse_string_outer(s, FLAG_PARSE_SEMICOLON | +				    FLAG_EXIT_FROM_LOOP); +# endif + +# ifdef CONFIG_AUTOBOOT_KEYED +		disable_ctrlc(prev);	/* restore Control C checking */ +# endif +	} +#endif	/* CONFIG_BOOTDELAY */ + +	/* +	 * Main Loop for Monitor Command Processing +	 */ +#ifdef CFG_HUSH_PARSER +	parse_file_outer(); +	/* This point is never reached */ +	for (;;); +#else +	for (;;) { +#ifdef CONFIG_BOOT_RETRY_TIME +		if (rc >= 0) { +			/* Saw enough of a valid command to +			 * restart the timeout. +			 */ +			reset_cmd_timeout(); +		} +#endif +		len = readline (CFG_PROMPT); + +		flag = 0;	/* assume no special flags for now */ +		if (len > 0) +			strcpy (lastcommand, console_buffer); +		else if (len == 0) +			flag |= CMD_FLAG_REPEAT; +#ifdef CONFIG_BOOT_RETRY_TIME +		else if (len == -2) { +			/* -2 means timed out, retry autoboot +			 */ +			printf("\nTimed out waiting for command\n"); +# ifdef CONFIG_RESET_TO_RETRY +			/* Reinit board to run initialization code again */ +			do_reset (NULL, 0, 0, NULL); +# else +			return;		/* retry autoboot */ +# endif +		} +#endif + +		if (len == -1) +			printf ("<INTERRUPT>\n"); +		else +			rc = run_command (lastcommand, flag); + +		if (rc <= 0) { +			/* invalid command or not repeatable, forget it */ +			lastcommand[0] = 0; +		} +	} +#endif /*CFG_HUSH_PARSER*/ +} + +/*************************************************************************** + * reset command line timeout to retry_time seconds + */ +#ifdef CONFIG_BOOT_RETRY_TIME +void reset_cmd_timeout(void) +{ +	endtime = endtick(retry_time); +} +#endif + +/****************************************************************************/ + +/* + * Prompt for input and read a line. + * If  CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0, + * time out when time goes past endtime (timebase time in ticks). + * Return:	number of read characters + *		-1 if break + *		-2 if timed out + */ +int readline (const char *const prompt) +{ +	char   *p = console_buffer; +	int	n = 0;				/* buffer index		*/ +	int	plen = 0;			/* prompt length	*/ +	int	col;				/* output column cnt	*/ +	char	c; + +	/* print prompt */ +	if (prompt) { +		plen = strlen (prompt); +		puts (prompt); +	} +	col = plen; + +	for (;;) { +#ifdef CONFIG_BOOT_RETRY_TIME +		while (!tstc()) {	/* while no incoming data */ +			if (retry_time >= 0 && get_ticks() > endtime) +				return (-2);	/* timed out */ +		} +#endif +		WATCHDOG_RESET();		/* Trigger watchdog, if needed */ + +#ifdef CONFIG_SHOW_ACTIVITY +		while (!tstc()) { +			extern void show_activity(int arg); +			show_activity(0); +		} +#endif +		c = getc(); + +		/* +		 * Special character handling +		 */ +		switch (c) { +		case '\r':				/* Enter		*/ +		case '\n': +			*p = '\0'; +			puts ("\r\n"); +			return (p - console_buffer); + +		case 0x03:				/* ^C - break		*/ +			console_buffer[0] = '\0';	/* discard input */ +			return (-1); + +		case 0x15:				/* ^U - erase line	*/ +			while (col > plen) { +				puts (erase_seq); +				--col; +			} +			p = console_buffer; +			n = 0; +			continue; + +		case 0x17:				/* ^W - erase word 	*/ +			p=delete_char(console_buffer, p, &col, &n, plen); +			while ((n > 0) && (*p != ' ')) { +				p=delete_char(console_buffer, p, &col, &n, plen); +			} +			continue; + +		case 0x08:				/* ^H  - backspace	*/ +		case 0x7F:				/* DEL - backspace	*/ +			p=delete_char(console_buffer, p, &col, &n, plen); +			continue; + +		default: +			/* +			 * Must be a normal character then +			 */ +			if (n < CFG_CBSIZE-2) { +				if (c == '\t') {	/* expand TABs		*/ +					puts (tab_seq+(col&07)); +					col += 8 - (col&07); +				} else { +					++col;		/* echo input		*/ +					putc (c); +				} +				*p++ = c; +				++n; +			} else {			/* Buffer full		*/ +				putc ('\a'); +			} +		} +	} +} + +/****************************************************************************/ + +static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen) +{ +	char *s; + +	if (*np == 0) { +		return (p); +	} + +	if (*(--p) == '\t') {			/* will retype the whole line	*/ +		while (*colp > plen) { +			puts (erase_seq); +			(*colp)--; +		} +		for (s=buffer; s<p; ++s) { +			if (*s == '\t') { +				puts (tab_seq+((*colp) & 07)); +				*colp += 8 - ((*colp) & 07); +			} else { +				++(*colp); +				putc (*s); +			} +		} +	} else { +		puts (erase_seq); +		(*colp)--; +	} +	(*np)--; +	return (p); +} + +/****************************************************************************/ + +int parse_line (char *line, char *argv[]) +{ +	int nargs = 0; + +#ifdef DEBUG_PARSER +	printf ("parse_line: \"%s\"\n", line); +#endif +	while (nargs < CFG_MAXARGS) { + +		/* skip any white space */ +		while ((*line == ' ') || (*line == '\t')) { +			++line; +		} + +		if (*line == '\0') {	/* end of line, no more args	*/ +			argv[nargs] = NULL; +#ifdef DEBUG_PARSER +		printf ("parse_line: nargs=%d\n", nargs); +#endif +			return (nargs); +		} + +		argv[nargs++] = line;	/* begin of argument string	*/ + +		/* find end of string */ +		while (*line && (*line != ' ') && (*line != '\t')) { +			++line; +		} + +		if (*line == '\0') {	/* end of line, no more args	*/ +			argv[nargs] = NULL; +#ifdef DEBUG_PARSER +		printf ("parse_line: nargs=%d\n", nargs); +#endif +			return (nargs); +		} + +		*line++ = '\0';		/* terminate current arg	 */ +	} + +	printf ("** Too many args (max. %d) **\n", CFG_MAXARGS); + +#ifdef DEBUG_PARSER +	printf ("parse_line: nargs=%d\n", nargs); +#endif +	return (nargs); +} + +/****************************************************************************/ + +static void process_macros (const char *input, char *output) +{ +	char c, prev; +	const char *varname_start = NULL; +	int inputcnt  = strlen (input); +	int outputcnt = CFG_CBSIZE; +	int state = 0;	/* 0 = waiting for '$'	*/ +			/* 1 = waiting for '('	*/ +			/* 2 = waiting for ')'	*/ + +#ifdef DEBUG_PARSER +	char *output_start = output; + +	printf ("[PROCESS_MACROS] INPUT len %d: \"%s\"\n", strlen(input), input); +#endif + +	prev = '\0';			/* previous character	*/ + +	while (inputcnt && outputcnt) { +	    c = *input++; +	    inputcnt--; + +	    /* remove one level of escape characters */ +	    if ((c == '\\') && (prev != '\\')) { +		if (inputcnt-- == 0) +			break; +		prev = c; +	    	c = *input++; +	    } + +	    switch (state) { +	    case 0:			/* Waiting for (unescaped) $	*/ +		if ((c == '$') && (prev != '\\')) { +			state++; +		} else { +			*(output++) = c; +			outputcnt--; +		} +		break; +	    case 1:			/* Waiting for (	*/ +		if (c == '(') { +			state++; +			varname_start = input; +		} else { +			state = 0; +			*(output++) = '$'; +			outputcnt--; + +			if (outputcnt) { +				*(output++) = c; +				outputcnt--; +			} +		} +		break; +	    case 2:			/* Waiting for )	*/ +		if (c == ')') { +			int i; +			char envname[CFG_CBSIZE], *envval; +			int envcnt = input-varname_start-1; /* Varname # of chars */ + +			/* Get the varname */ +			for (i = 0; i < envcnt; i++) { +				envname[i] = varname_start[i]; +			} +			envname[i] = 0; + +			/* Get its value */ +			envval = getenv (envname); + +			/* Copy into the line if it exists */ +			if (envval != NULL) +				while ((*envval) && outputcnt) { +					*(output++) = *(envval++); +					outputcnt--; +				} +			/* Look for another '$' */ +			state = 0; +		} +		break; +	    } + +	    prev = c; +	} + +	if (outputcnt) +		*output = 0; + +#ifdef DEBUG_PARSER +	printf ("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n", +		strlen(output_start), output_start); +#endif +} + +/**************************************************************************** + * returns: + *	1  - command executed, repeatable + *	0  - command executed but not repeatable, interrupted commands are + *	     always considered not repeatable + *	-1 - not executed (unrecognized, bootd recursion or too many args) + *           (If cmd is NULL or "" or longer than CFG_CBSIZE-1 it is + *           considered unrecognized) + * + * WARNING: + * + * We must create a temporary copy of the command since the command we get + * may be the result from getenv(), which returns a pointer directly to + * the environment data, which may change magicly when the command we run + * creates or modifies environment variables (like "bootp" does). + */ + +int run_command (const char *cmd, int flag) +{ +	cmd_tbl_t *cmdtp; +	char cmdbuf[CFG_CBSIZE];	/* working copy of cmd		*/ +	char *token;			/* start of token in cmdbuf	*/ +	char *sep;			/* end of token (separator) in cmdbuf */ +	char finaltoken[CFG_CBSIZE]; +	char *str = cmdbuf; +	char *argv[CFG_MAXARGS + 1];	/* NULL terminated	*/ +	int argc; +	int repeatable = 1; + +#ifdef DEBUG_PARSER +	printf ("[RUN_COMMAND] cmd[%p]=\"", cmd); +	puts (cmd ? cmd : "NULL");	/* use puts - string may be loooong */ +	puts ("\"\n"); +#endif + +	clear_ctrlc();		/* forget any previous Control C */ + +	if (!cmd || !*cmd) { +		return -1;	/* empty command */ +	} + +	if (strlen(cmd) >= CFG_CBSIZE) { +		puts ("## Command too long!\n"); +		return -1; +	} + +	strcpy (cmdbuf, cmd); + +	/* Process separators and check for invalid +	 * repeatable commands +	 */ + +#ifdef DEBUG_PARSER +	printf ("[PROCESS_SEPARATORS] %s\n", cmd); +#endif +	while (*str) { + +		/* +		 * Find separator, or string end +		 * Allow simple escape of ';' by writing "\;" +		 */ +		for (sep = str; *sep; sep++) { +			if ((*sep == ';') &&	/* separator		*/ +			    ( sep != str) &&	/* past string start	*/ +			    (*(sep-1) != '\\'))	/* and NOT escaped	*/ +				break; +		} + +		/* +		 * Limit the token to data between separators +		 */ +		token = str; +		if (*sep) { +			str = sep + 1;	/* start of command for next pass */ +			*sep = '\0'; +		} +		else +			str = sep;	/* no more commands for next pass */ +#ifdef DEBUG_PARSER +		printf ("token: \"%s\"\n", token); +#endif + +		/* find macros in this token and replace them */ +		process_macros (token, finaltoken); + +		/* Extract arguments */ +		argc = parse_line (finaltoken, argv); + +		/* Look up command in command table */ +		if ((cmdtp = find_cmd(argv[0])) == NULL) { +			printf ("Unknown command '%s' - try 'help'\n", argv[0]); +			return -1;	/* give up after bad command */ +		} + +		/* found - check max args */ +		if (argc > cmdtp->maxargs) { +			printf ("Usage:\n%s\n", cmdtp->usage); +			return -1; +		} + +#if (CONFIG_COMMANDS & CFG_CMD_BOOTD) +		/* avoid "bootd" recursion */ +		if (cmdtp->cmd == do_bootd) { +#ifdef DEBUG_PARSER +			printf ("[%s]\n", finaltoken); +#endif +			if (flag & CMD_FLAG_BOOTD) { +				printf ("'bootd' recursion detected\n"); +				return -1; +			} +			else +				flag |= CMD_FLAG_BOOTD; +		} +#endif	/* CFG_CMD_BOOTD */ + +		/* OK - call function to do the command */ +		if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) { +			return (-1); +		} + +		repeatable &= cmdtp->repeatable; + +		/* Did the user stop this? */ +		if (had_ctrlc ()) +			return 0;	/* if stopped then not repeatable */ +	} + +	return repeatable; +} + +/****************************************************************************/ + +#if (CONFIG_COMMANDS & CFG_CMD_RUN) +int do_run (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ +	int i; +	int rcode = 1; + +	if (argc < 2) { +		printf ("Usage:\n%s\n", cmdtp->usage); +		return 1; +	} + +	for (i=1; i<argc; ++i) { +#ifndef CFG_HUSH_PARSER +	    if (run_command (getenv (argv[i]), flag) != -1) ++rcode; +#else +   	    if (parse_string_outer(getenv (argv[i]), +		    FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP) == 0) ++rcode; +#endif +	} +	return ((rcode == i) ? 0 : 1); +} +#endif |