diff options
| author | wdenk <wdenk> | 2002-12-17 01:51:00 +0000 | 
|---|---|---|
| committer | wdenk <wdenk> | 2002-12-17 01:51:00 +0000 | 
| commit | 6aff3115b90780933d390d2b471479179927468d (patch) | |
| tree | ea9e54617d8a6f6b2d05d37c7f2bde24ce6dfb35 /tools/env/fw_env.c | |
| parent | 228f29ac6e0026e596b3a6fbb640118b9944cdd8 (diff) | |
| download | olio-uboot-2014.01-6aff3115b90780933d390d2b471479179927468d.tar.xz olio-uboot-2014.01-6aff3115b90780933d390d2b471479179927468d.zip | |
* Use 1-byte-read instead of -write for iprobe() function
  Add i2c commands to PM826 config
* extend I2C POST code: check for list on known addresses
Diffstat (limited to 'tools/env/fw_env.c')
| -rw-r--r-- | tools/env/fw_env.c | 662 | 
1 files changed, 662 insertions, 0 deletions
| diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c new file mode 100644 index 000000000..5bf75ac2c --- /dev/null +++ b/tools/env/fw_env.c @@ -0,0 +1,662 @@ +/* + * (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 <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <unistd.h> +#include <linux/mtd/mtd.h> +#include "fw_env.h" + +typedef	unsigned char	uchar; + +#define	CMD_GETENV	"fw_printenv" +#define	CMD_SETENV	"fw_setenv" + +typedef struct envdev_s { +	uchar devname[16]; /* Device name */ +	ulong env_size;    /* environment size */ +	ulong erase_size;  /* device erase size */ +} envdev_t; + +static envdev_t envdevices[2]; +static int curdev; + +#define DEVNAME(i)    envdevices[(i)].devname +#define ENVSIZE(i)    envdevices[(i)].env_size +#define DEVESIZE(i)   envdevices[(i)].erase_size + +#define CFG_ENV_SIZE ENVSIZE(curdev) + +#ifdef HAVE_REDUND +#define ENV_SIZE (CFG_ENV_SIZE - sizeof(long) - 1) +#else +#define ENV_SIZE (CFG_ENV_SIZE - sizeof(long)) +#endif + +typedef struct environment_s { +	ulong	crc;		/* CRC32 over data bytes	*/ +	uchar   flags;      /* active or obsolete */ +	uchar *data; +} env_t; + +static env_t environment; +static int valid = 0; + +#ifdef HAVE_REDUND +static uchar active_flag = 1; +static uchar obsolete_flag = 0; +#endif + +#define XMK_STR(x)	#x +#define MK_STR(x)	XMK_STR(x) + +static uchar default_environment[] = { +#ifdef	CONFIG_BOOTARGS +	"bootargs="	CONFIG_BOOTARGS			"\0" +#endif +#ifdef	CONFIG_BOOTCOMMAND +	"bootcmd="	CONFIG_BOOTCOMMAND		"\0" +#endif +#if (CONFIG_BOOTDELAY >= 0) +	"bootdelay="	MK_STR(CONFIG_BOOTDELAY)	"\0" +#endif +#if (CONFIG_BAUDRATE >= 0) +	"baudrate="	MK_STR(CONFIG_BAUDRATE)		"\0" +#endif +#ifdef	CONFIG_ETHADDR +	"ethaddr="	MK_STR(CONFIG_ETHADDR)		"\0" +#endif +#ifdef	CONFIG_IPADDR +	"ipaddr="	MK_STR(CONFIG_IPADDR)		"\0" +#endif +#ifdef	CONFIG_SERVERIP +	"serverip="	MK_STR(CONFIG_SERVERIP)		"\0" +#endif +	"\0" +}; + +static int  flash_io (int mode); +static uchar *envmatch(uchar *s1, uchar *s2); +static int env_init(void); +static int parse_config(void); + + +/* + * Search the environment for a variable. + * Return the value, if found, or NULL, if not found. + */ +unsigned char *fw_getenv (unsigned char *name) +{ +	uchar *env, *nxt; + +	if (env_init()) +		return (NULL); + +	for (env=environment.data; *env; env=nxt+1) { +		uchar *val; + +		for (nxt=env; *nxt; ++nxt) { +			if (nxt >= &environment.data[ENV_SIZE]) { +				fprintf (stderr, "## Error: " +					"environment not terminated\n"); +				return (NULL); +			} +		} +		val=envmatch(name, env); +		if (!val) +			continue; +		return (val); +	} +	return (NULL); +} + +/* + * Print the current definition of one, or more, or all + * environment variables + */ +void fw_printenv(int argc, char *argv[]) +{ +	uchar *env, *nxt; +	int i, n_flag; + +	if (env_init()) +		return; + +	if (argc == 1) {		/* Print all env variables	*/ +		for (env=environment.data; *env; env=nxt+1) { +			for (nxt=env; *nxt; ++nxt) { +				if (nxt >= &environment.data[ENV_SIZE]) { +					fprintf (stderr, "## Error: " +						"environment not terminated\n"); +					return; +				} +			} + +			printf("%s\n", env); +		} +		return; +	} + +	if (strcmp(argv[1], "-n") == 0) { +		n_flag = 1; +		++argv; +		--argc; +		if (argc != 2) { +			fprintf (stderr, "## Error: " +				"`-n' option requires exactly one argument\n"); +			return; +		} +	} else { +		n_flag = 0; +	} + +	for (i=1; i<argc; ++i) {	/* print single env variables	*/ +		uchar *name = argv[i]; +		uchar *val = NULL; + +		for (env=environment.data; *env; env=nxt+1) { + +			for (nxt=env; *nxt; ++nxt) { +				if (nxt >= &environment.data[ENV_SIZE]) { +					fprintf (stderr, "## Error: " +						"environment not terminated\n"); +					return; +				} +			} +			val=envmatch(name, env); +			if (val) { +				if (!n_flag) { +					fputs (name, stdout); +					putc  ('=',  stdout); +				} +				puts  (val); +				break; +			} +		} +		if (!val) +			fprintf (stderr, "## Error: \"%s\" not defined\n", +			 name); +	} +} + +/* + * Deletes or sets environment variables. Returns errno style error codes: + * 0	  - OK + * EINVAL - need at least 1 argument + * EROFS  - certain variables ("ethaddr", "serial#") cannot be + *	    modified or deleted + * + */ +int fw_setenv (int argc, char *argv[]) +{ +	int  i, len; +	uchar *env, *nxt; +	uchar *oldval = NULL; +	uchar *name; + +	if (argc < 2) { +		return (EINVAL); +	} + +	if (env_init()) +		return (errno); + +	name = argv[1]; + +	/* +	 * search if variable with this name already exists +	 */ +	for (env=environment.data; *env; env=nxt+1) { +		for (nxt=env; *nxt; ++nxt) { +			if (nxt >= &environment.data[ENV_SIZE]) { +				fprintf (stderr, "## Error: " +					"environment not terminated\n"); +				return (EINVAL); +			} +		} +		if ((oldval=envmatch(name, env)) != NULL) +			break; +	} + +	/* +	 * Delete any existing definition +	 */ +	if (oldval) { +		/* +		 * Ethernet Address and serial# can be set only once +		 */ +		if ((strcmp (name, "ethaddr") == 0) || +		    (strcmp (name, "serial#") == 0) ) { +			fprintf (stderr, "Can't overwrite \"%s\"\n", name); +			return (EROFS); +		} + +		if (*++nxt == '\0') { +			*env = '\0'; +		} else { +			for (;;) { +				*env = *nxt++; +				if ((*env == '\0') && (*nxt == '\0')) +					break; +				++env; +			} +		} +		*++env = '\0'; +	} + +	/* Delete only ? */ +	if (argc < 3) +		goto WRITE_FLASH; + +	/* +	 * Append new definition at the end +	 */ +	for (env=environment.data; *env || *(env+1); ++env) +		; +	if (env > environment.data) +		++env; +	/* +	 * Overflow when: +	 * "name" + "=" + "val" +"\0\0"  > CFG_ENV_SIZE - (env-environment) +	 */ +	len = strlen(name) + 2; +	/* add '=' for first arg, ' ' for all others */ +	for (i=2; i<argc; ++i) { +		len += strlen(argv[i]) + 1; +	} +	if (len > (&environment.data[ENV_SIZE]-env)) { +		fprintf (stderr, +			"Error: environment overflow, \"%s\" deleted\n", +			name); +		return (-1); +	} +	while ((*env = *name++) != '\0') +		env++; +	for (i=2; i<argc; ++i) { +		uchar *val = argv[i]; + +		*env = (i==2) ? '=' : ' '; +		while ((*++env = *val++) != '\0') +			; +	} + +	/* end is marked with double '\0' */ +	*++env = '\0'; + +WRITE_FLASH: + +	/* Update CRC */ +	environment.crc = crc32(0, environment.data, ENV_SIZE); + +	/* write environment back to flash */ +	if (flash_io (O_RDWR)) { +		fprintf (stderr, +			"Error: can't write fw_env to flash\n"); +		return (-1); +	} + +	return (0); +} + +static int flash_io (int mode) +{ +	int fd, fdr, rc, otherdev, len, resid; +	erase_info_t erase; +	char *data; + +	if ((fd = open(DEVNAME(curdev), mode)) < 0) { +		fprintf (stderr,  +				 "Can't open %s: %s\n",  +				 DEVNAME(curdev), strerror(errno)); +		return (-1); +	} + +	len = sizeof(environment.crc) + sizeof(environment.flags); + +	if (mode == O_RDWR) { +#ifdef HAVE_REDUND +		/* switch to next partition for writing */ +		otherdev = !curdev; +		if ((fdr = open(DEVNAME(otherdev), mode)) < 0) { +			fprintf (stderr,  +					 "Can't open %s: %s\n",  +					 DEVNAME(otherdev), strerror(errno)); +			return (-1); +		} +#else +		otherdev = curdev; +		fdr = fd; +		len = sizeof(environment.crc); +#endif +		printf("Unlocking flash...\n"); +		erase.length = DEVESIZE(otherdev); +		erase.start = 0; +		ioctl (fdr, MEMUNLOCK, &erase); + +#ifdef HAVE_REDUND +		erase.length = DEVESIZE(curdev); +		erase.start = 0; +		ioctl (fd, MEMUNLOCK, &erase); +		environment.flags = active_flag; +#endif +		printf("Done\n"); +		resid = DEVESIZE(otherdev) - CFG_ENV_SIZE; +		if (resid) { +			if ((data = malloc(resid)) == NULL) { +				fprintf(stderr,  +				  "Cannot malloc %d bytes: %s\n", +				  resid, strerror(errno)); +				return (-1); +			} +			if (lseek (fdr, CFG_ENV_SIZE, SEEK_SET) == -1) { +				fprintf (stderr, +				  "seek error on %s: %s\n",  +				   DEVNAME(curdev), strerror(errno)); +				return (-1); +			} +			if ((rc = read (fdr, data, resid)) != resid) { +				fprintf (stderr, +				  "read error on %s: %s\n",  +				  DEVNAME(curdev), strerror(errno)); +				return (-1); +			} +			if (lseek (fdr, 0, SEEK_SET) == -1) { +				fprintf (stderr, +				  "seek error on %s: %s\n",  +				  DEVNAME(curdev), strerror(errno)); +				return (-1); +			} +		} + +		printf("Erasing old environment...\n"); + +		erase.length = DEVESIZE(otherdev); +		if (ioctl (fdr, MEMERASE, &erase) != 0) { +			fprintf (stderr, "MTD erase error on %s: %s\n", +			  DEVNAME(otherdev), strerror(errno)); +			return (-1); +		} + +		printf("Done\n"); + +		printf("Writing environment to %s...\n",DEVNAME(otherdev)); +		if (write(fdr, &environment, len) != len) { +			fprintf (stderr, +			  "CRC write error on %s: %s\n",  +			  DEVNAME(otherdev), strerror(errno)); +			return (-1); +		} +		if (write(fdr, environment.data, ENV_SIZE) != ENV_SIZE) { +			fprintf (stderr, +			  "Write error on %s: %s\n",  +			  DEVNAME(otherdev), strerror(errno)); +			return (-1); +		} +		if (resid) { +			if (write (fdr, data, resid) != resid) { +				fprintf (stderr, +				 "write error on %s: %s\n",  +				  DEVNAME(curdev), strerror(errno)); +				return (-1); +			} +			free(data); +		} +#ifdef HAVE_REDUND +		/* change flag on current active env partition */ +		if (lseek (fd, sizeof(ulong), SEEK_SET) == -1) { +			fprintf (stderr, +			  "seek error on %s: %s\n",  +			  DEVNAME(curdev), strerror(errno)); +			return (-1); +		} +		if (write (fd, &obsolete_flag, sizeof(obsolete_flag)) !=  +			sizeof(obsolete_flag)) { +			fprintf (stderr, +			 "Write error on %s: %s\n",  +			 DEVNAME(curdev), strerror(errno)); +			return (-1); +		} +#endif +		printf("Done\n"); +		printf("Locking ...\n"); +		erase.length = DEVESIZE(otherdev); +		erase.start = 0; +		ioctl (fdr, MEMLOCK, &erase); +#ifdef HAVE_REDUND +		erase.length = DEVESIZE(curdev); +		erase.start = 0; +		ioctl (fd, MEMLOCK, &erase); +		if (close(fdr)) { +			fprintf (stderr, +			 "I/O error on %s: %s\n",  +			 DEVNAME(otherdev), strerror(errno)); +			return (-1); +		} +#endif +		printf("Done\n"); +	} else { +#ifndef HAVE_REDUND +		len = sizeof(environment.crc); +#endif +		if (read (fd, &environment, len) != len) { +			fprintf (stderr, +			 "CRC read error on %s: %s\n",  +			 DEVNAME(curdev), strerror(errno)); +			return (-1); +		} +		if ((rc = read (fd, environment.data, ENV_SIZE)) != ENV_SIZE) { +			fprintf (stderr, +			 "Read error on %s: %s\n",  +			  DEVNAME(curdev), strerror(errno)); +			return (-1); +		} +	} + +	if (close(fd)) { +		fprintf (stderr, +		  "I/O error on %s: %s\n",  +		  DEVNAME(curdev), strerror(errno)); +		return (-1); +	} + +	/* everything ok */ +	return (0); +} + +/* + * s1 is either a simple 'name', or a 'name=value' pair. + * s2 is a 'name=value' pair. + * If the names match, return the value of s2, else NULL. + */ + +static uchar * +envmatch (uchar *s1, uchar *s2) +{ + +	while (*s1 == *s2++) +		if (*s1++ == '=') +			return(s2); +	if (*s1 == '\0' && *(s2-1) == '=') +		return(s2); +	return(NULL); +} + +/* + * Prevent confusion if running from erased flash memory + */ +static int env_init(void) +{ +	int crc1, crc1_ok; +	uchar *addr1; +#ifdef HAVE_REDUND +	int crc2, crc2_ok; +	uchar flag1, flag2, *addr2; +#endif + +	 +	if (!valid) { + +		if (parse_config()) /* should fill envdevices */ +			return 1; + +		if ((addr1 = calloc (1, ENV_SIZE)) == NULL) { +			fprintf (stderr,  +			 "Not enough memory for environment (%ld bytes)\n", +			 ENV_SIZE); +			return (errno); +		} + +		/* read environment from FLASH to local buffer */ +		environment.data = addr1; +		curdev = 0; +		if (flash_io (O_RDONLY)) { +			return (errno); +		} + +		crc1_ok = ((crc1 = crc32(0, environment.data, ENV_SIZE))  +			 == environment.crc); +#ifndef HAVE_REDUND +		if (!crc1_ok) { +			fprintf (stderr,  +			 "Warning: Bad CRC, using default environment\n"); +			environment.data = default_environment; +			free(addr1); +		} +#else +		flag1 = environment.flags; +		 +		curdev = 1; +		if ((addr2 = calloc (1, ENV_SIZE)) == NULL) { +			fprintf (stderr,  +			 "Not enough memory for environment (%ld bytes)\n", +			 ENV_SIZE); +			return (errno); +		}		 +		environment.data = addr2; + +		if (flash_io (O_RDONLY)) { +			return (errno); +		} + +		crc2_ok = ((crc2 = crc32(0, environment.data, ENV_SIZE))  +				   == environment.crc); +		flag2 = environment.flags; +		 +		if (crc1_ok && ! crc2_ok) { +			environment.data  = addr1; +			environment.flags = flag1; +			environment.crc = crc1; +			curdev = 0; +			free(addr2); +		} +		else if (! crc1_ok && crc2_ok) { +			environment.data  = addr2; +			environment.flags = flag2; +			environment.crc = crc2; +			curdev = 1; +			free(addr1); +		} +		else if (! crc1_ok && ! crc2_ok) { +			fprintf (stderr,  +			 "Warning: Bad CRC, using default environment\n"); +			environment.data = default_environment; +			curdev = 0; +			free(addr2); +			free(addr1); +		} +		else if (flag1 == active_flag && flag2 == obsolete_flag) { +			environment.data  = addr1; +			environment.flags = flag1; +			environment.crc = crc1; +			curdev = 0; +			free(addr2); +		} +		else if (flag1 == obsolete_flag && flag2 == active_flag) { +			environment.data  = addr2; +			environment.flags = flag2; +			environment.crc = crc2; +			curdev = 1; +			free(addr1); +		} +		else if (flag1 == flag2) { +			environment.data  = addr1; +			environment.flags = flag1; +			environment.crc = crc1; +			curdev = 0; +			free(addr2); +		} +		else if (flag1 == 0xFF)	{ +			environment.data  = addr1; +			environment.flags = flag1; +			environment.crc = crc1; +			curdev = 0; +			free(addr2); +		} +		else if (flag2 == 0xFF) { +			environment.data  = addr2; +			environment.flags = flag2; +			environment.crc = crc2; +			curdev = 1; +			free(addr1); +		} +#endif		 +		valid = 1; +	} +	return (0); +} + + +static int parse_config() +{ +	struct stat st; + +	if (stat (DEVICE1_NAME, &st)) { +		fprintf (stderr,  +		 "Cannot access MTD device %s: %s\n",  +		 DEVICE1_NAME, strerror(errno)); +		return 1; +	} + +	strcpy(DEVNAME(0), DEVICE1_NAME); +	ENVSIZE(0) = ENV1_SIZE; +	DEVESIZE(0) = DEVICE1_ESIZE; +#ifdef HAVE_REDUND +	if (stat (DEVICE2_NAME, &st)) { +		fprintf (stderr,  +		 "Cannot access MTD device %s: %s\n",  +		 DEVICE2_NAME, strerror(errno)); +		return 1; +	} +	strcpy(DEVNAME(1), DEVICE2_NAME); +	ENVSIZE(1) = ENV2_SIZE; +	DEVESIZE(1) = DEVICE2_ESIZE; +#endif +	return 0; +} |