]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - tools/env/fw_env.c
tools/env/fw_printenv: Make redundant env work on locked flashes also
[karo-tx-uboot.git] / tools / env / fw_env.c
index a46205d868735b17b2686956d2a52efcd0f29fa7..8ff70522430dee31f776a6f31c8eae7442df83de 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * (C) Copyright 2000-2008
+ * (C) Copyright 2000-2010
  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
  *
  * (C) Copyright 2008
@@ -45,8 +45,7 @@
 
 #include "fw_env.h"
 
-#define        CMD_GETENV      "fw_printenv"
-#define        CMD_SETENV      "fw_setenv"
+#define WHITESPACE(c) ((c == '\t') || (c == ' '))
 
 #define min(x, y) ({                           \
        typeof(x) _min1 = (x);                  \
@@ -210,7 +209,6 @@ static char default_environment[] = {
 
 static int flash_io (int mode);
 static char *envmatch (char * s1, char * s2);
-static int env_init (void);
 static int parse_config (void);
 
 #if defined(CONFIG_FILE)
@@ -225,6 +223,22 @@ static inline ulong getenvsize (void)
        return rc;
 }
 
+static char *fw_string_blank(char *s, int noblank)
+{
+       int i;
+       int len = strlen(s);
+
+       for (i = 0; i < len; i++, s++) {
+               if ((noblank && !WHITESPACE(*s)) ||
+                       (!noblank && WHITESPACE(*s)))
+                       break;
+       }
+       if (i == len)
+               return NULL;
+
+       return s;
+}
+
 /*
  * Search the environment for a variable.
  * Return the value, if found, or NULL, if not found.
@@ -233,7 +247,7 @@ char *fw_getenv (char *name)
 {
        char *env, *nxt;
 
-       if (env_init ())
+       if (fw_env_open())
                return NULL;
 
        for (env = environment.data; *env; env = nxt + 1) {
@@ -264,7 +278,7 @@ int fw_printenv (int argc, char *argv[])
        int i, n_flag;
        int rc = 0;
 
-       if (env_init ())
+       if (fw_env_open())
                return -1;
 
        if (argc == 1) {                /* Print all env variables  */
@@ -327,30 +341,34 @@ int fw_printenv (int argc, char *argv[])
        return rc;
 }
 
-/*
- * Deletes or sets environment variables. Returns -1 and sets errno 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 fw_env_close(void)
 {
-       int i, len;
-       char *env, *nxt;
-       char *oldval = NULL;
-       char *name;
+       /*
+        * Update CRC
+        */
+       *environment.crc = crc32(0, (uint8_t *) environment.data, ENV_SIZE);
 
-       if (argc < 2) {
-               errno = EINVAL;
-               return -1;
+       /* write environment back to flash */
+       if (flash_io(O_RDWR)) {
+               fprintf(stderr,
+                       "Error: can't write fw_env to flash\n");
+                       return -1;
        }
 
-       if (env_init ())
-               return -1;
+       return 0;
+}
 
-       name = argv[1];
+
+/*
+ * Set/Clear a single variable in the environment.
+ * This is called in sequence to update the environment
+ * in RAM without updating the copy in flash after each set
+ */
+int fw_env_write(char *name, char *value)
+{
+       int len;
+       char *env, *nxt;
+       char *oldval = NULL;
 
        /*
         * search if variable with this name already exists
@@ -358,7 +376,7 @@ int fw_setenv (int argc, char *argv[])
        for (nxt = env = environment.data; *env; env = nxt + 1) {
                for (nxt = env; *nxt; ++nxt) {
                        if (nxt >= &environment.data[ENV_SIZE]) {
-                               fprintf (stderr, "## Error: "
+                               fprintf(stderr, "## Error: "
                                        "environment not terminated\n");
                                errno = EINVAL;
                                return -1;
@@ -396,8 +414,8 @@ int fw_setenv (int argc, char *argv[])
        }
 
        /* Delete only ? */
-       if (argc < 3)
-               goto WRITE_FLASH;
+       if (!value || !strlen(value))
+               return 0;
 
        /*
         * Append new definition at the end
@@ -411,41 +429,202 @@ int fw_setenv (int argc, char *argv[])
         */
        len = strlen (name) + 2;
        /* add '=' for first arg, ' ' for all others */
-       for (i = 2; i < argc; ++i) {
-               len += strlen (argv[i]) + 1;
-       }
+       len += strlen(value) + 1;
+
        if (len > (&environment.data[ENV_SIZE] - env)) {
                fprintf (stderr,
                        "Error: environment overflow, \"%s\" deleted\n",
                        name);
                return -1;
        }
+
        while ((*env = *name++) != '\0')
                env++;
+       *env = '=';
+       while ((*++env = *value++) != '\0')
+               ;
+
+       /* end is marked with double '\0' */
+       *++env = '\0';
+
+       return 0;
+}
+
+/*
+ * Deletes or sets environment variables. Returns -1 and sets errno 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;
+       char *name;
+       char *value = NULL;
+       char *tmpval = NULL;
+
+       if (argc < 2) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (fw_env_open()) {
+               fprintf(stderr, "Error: environment not initialized\n");
+               return -1;
+       }
+
+       name = argv[1];
+
+       len = strlen(name) + 2;
+       for (i = 2; i < argc; ++i)
+               len += strlen(argv[i]) + 1;
+
+       /* Allocate enough place to the data string */
        for (i = 2; i < argc; ++i) {
                char *val = argv[i];
-
-               *env = (i == 2) ? '=' : ' ';
-               while ((*++env = *val++) != '\0');
+               if (!value) {
+                       value = (char *)malloc(len - strlen(name));
+                       if (!value) {
+                               fprintf(stderr,
+                               "Cannot malloc %u bytes: %s\n",
+                               len - strlen(name), strerror(errno));
+                               return -1;
+                       }
+                       memset(value, 0, len - strlen(name));
+                       tmpval = value;
+               }
+               if (i != 2)
+                       *tmpval++ = ' ';
+               while (*val != '\0')
+                       *tmpval++ = *val++;
        }
 
-       /* end is marked with double '\0' */
-       *++env = '\0';
+       fw_env_write(name, value);
 
-  WRITE_FLASH:
+       if (value)
+               free(value);
 
-       /*
-        * Update CRC
-        */
-       *environment.crc = crc32 (0, (uint8_t *) environment.data, ENV_SIZE);
+       return fw_env_close();
+}
 
-       /* write environment back to flash */
-       if (flash_io (O_RDWR)) {
-               fprintf (stderr, "Error: can't write fw_env to flash\n");
+/*
+ * Parse  a file  and configure the u-boot variables.
+ * The script file has a very simple format, as follows:
+ *
+ * Each line has a couple with name, value:
+ * <white spaces>variable_name<white spaces>variable_value
+ *
+ * Both variable_name and variable_value are interpreted as strings.
+ * Any character after <white spaces> and before ending \r\n is interpreted
+ * as variable's value (no comment allowed on these lines !)
+ *
+ * Comments are allowed if the first character in the line is #
+ *
+ * Returns -1 and sets errno error codes:
+ * 0     - OK
+ * -1     - Error
+ */
+int fw_parse_script(char *fname)
+{
+       FILE *fp;
+       char dump[1024];        /* Maximum line length in the file */
+       char *name;
+       char *val;
+       int lineno = 0;
+       int len;
+       int ret = 0;
+
+       if (fw_env_open()) {
+               fprintf(stderr, "Error: environment not initialized\n");
                return -1;
        }
 
-       return 0;
+       if (strcmp(fname, "-") == 0)
+               fp = stdin;
+       else {
+               fp = fopen(fname, "r");
+               if (fp == NULL) {
+                       fprintf(stderr, "I cannot open %s for reading\n",
+                                fname);
+                       return -1;
+               }
+       }
+
+       while (fgets(dump, sizeof(dump), fp)) {
+               lineno++;
+               len = strlen(dump);
+
+               /*
+                * Read a whole line from the file. If the line is too long
+                * or is not terminated, reports an error and exit.
+                */
+               if (dump[len - 1] != '\n') {
+                       fprintf(stderr,
+                       "Line %d not corrected terminated or too long\n",
+                               lineno);
+                       ret = -1;
+                       break;
+               }
+
+               /* Drop ending line feed / carriage return */
+               while (len > 0 && (dump[len - 1] == '\n' ||
+                               dump[len - 1] == '\r')) {
+                       dump[len - 1] = '\0';
+                       len--;
+               }
+
+               /* Skip comment or empty lines */
+               if ((len == 0) || dump[0] == '#')
+                       continue;
+
+               /*
+                * Search for variable's name,
+                * remove leading whitespaces
+                */
+               name = fw_string_blank(dump, 1);
+               if (!name)
+                       continue;
+
+               /* The first white space is the end of variable name */
+               val = fw_string_blank(name, 0);
+               len = strlen(name);
+               if (val) {
+                       *val++ = '\0';
+                       if ((val - name) < len)
+                               val = fw_string_blank(val, 1);
+                       else
+                               val = NULL;
+               }
+
+#ifdef DEBUG
+               fprintf(stderr, "Setting %s : %s\n",
+                       name, val ? val : " removed");
+#endif
+
+               /*
+                * If there is an error setting a variable,
+                * try to save the environment and returns an error
+                */
+               if (fw_env_write(name, val)) {
+                       fprintf(stderr,
+                       "fw_env_write returns with error : %s\n",
+                               strerror(errno));
+                       ret = -1;
+                       break;
+               }
+
+       }
+
+       /* Close file if not stdin */
+       if (strcmp(fname, "-") != 0)
+               fclose(fp);
+
+       ret |= fw_env_close();
+
+       return ret;
+
 }
 
 /*
@@ -720,7 +899,10 @@ static int flash_write_buf (int dev, int fd, void *buf, size_t count,
 static int flash_flag_obsolete (int dev, int fd, off_t offset)
 {
        int rc;
+       struct erase_info_user erase;
 
+       erase.start  = DEVOFFSET (dev);
+       erase.length = DEVESIZE (dev);
        /* This relies on the fact, that obsolete_flag == 0 */
        rc = lseek (fd, offset, SEEK_SET);
        if (rc < 0) {
@@ -728,7 +910,9 @@ static int flash_flag_obsolete (int dev, int fd, off_t offset)
                         DEVNAME (dev));
                return rc;
        }
+       ioctl (fd, MEMUNLOCK, &erase);
        rc = write (fd, &obsolete_flag, sizeof (obsolete_flag));
+       ioctl (fd, MEMLOCK, &erase);
        if (rc < 0)
                perror ("Could not set obsolete flag");
 
@@ -880,7 +1064,7 @@ static char *envmatch (char * s1, char * s2)
 /*
  * Prevent confusion if running from erased flash memory
  */
-static int env_init (void)
+int fw_env_open(void)
 {
        int crc0, crc0_ok;
        char flag0;