diff packages/redboot/current/src/fconfig.c @ 1223:dbcfe4e0dbd2

New platform - TAMS MOAB - and generic changes to support it
author gthomas
date Fri, 19 Sep 2003 17:11:18 +0000
parents
children 89db0c354b7b
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/redboot/current/src/fconfig.c
@@ -0,0 +1,1015 @@
+//==========================================================================
+//
+//      fconfig.c
+//
+//      RedBoot - persistent data storage support (FLASH or EEPROM)
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+// Copyright (C) 2003 Gary Thomas
+//
+// eCos 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 or (at your option) any later version.
+//
+// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license/
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    gthomas
+// Contributors: gthomas, tkoeller
+// Date:         2000-07-28
+// Purpose:      
+// Description:  
+//              
+// This code is part of RedBoot (tm).
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <redboot.h>
+#include <cyg/io/flash.h>
+#include <fis.h>
+
+#ifdef CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
+// Note horrid intertwining of functions, to save precious FLASH
+#endif
+
+#ifdef CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_EEPROM
+externC void write_eeprom(void *buf, int len);
+externC void read_eeprom(void *buf, int len);
+#endif
+
+// Round a quantity up
+#define _rup(n,s) ((((n)+(s-1))/s)*s)
+
+#include <flash_config.h>
+
+// Configuration data, saved in FLASH, used to set/update RedBoot
+// normal "configuration" data items.
+struct _config *config, *backup_config;
+
+// Local data used by these routines
+#ifdef CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH
+extern void *flash_start, *flash_end;
+extern int flash_block_size, flash_num_blocks;
+extern int __flash_init;
+#ifdef CYGOPT_REDBOOT_FIS
+extern void *fis_work_block;
+extern void *fis_addr;
+extern int fisdir_size;  // Size of FIS directory.
+#endif
+#ifdef CYGSEM_REDBOOT_FLASH_CONFIG_READONLY_FALLBACK
+static struct _config  *readonly_config;
+#endif
+void *cfg_base;   // Location in Flash of config data
+int   cfg_size;   // Length of config data - rounded to Flash block size
+#endif // FLASH MEDIA
+
+// Prototypes for local functions
+static unsigned char *flash_lookup_config(char *key);
+
+static bool config_ok;
+
+#define CONFIG_KEY1    0x0BADFACE
+#define CONFIG_KEY2    0xDEADDEAD
+
+#define CONFIG_DONE    0
+#define CONFIG_ABORT  -1
+#define CONFIG_CHANGED 1
+#define CONFIG_OK      2
+#define CONFIG_BACK    3
+#define CONFIG_BAD     4
+
+// Note: the following options are related.  If 'boot_script' is false, then
+// the other values are used in the configuration.  Because of the way
+// that configuration tables are generated, they should have names which
+// are related.  The configuration options will show up lexicographically
+// ordered, thus the peculiar naming.
+RedBoot_config_option("Run script at boot",
+                      boot_script,
+                      ALWAYS_ENABLED, true,
+                      CONFIG_BOOL,
+                      false
+    );
+RedBoot_config_option("Boot script",
+                      boot_script_data,
+                      "boot_script", true,
+                      CONFIG_SCRIPT,
+                      ""
+    );
+// Some preprocessor magic for building the [constant] prompt string
+#define __cat(s1,c2,s3) s1 #c2 s3
+#define _cat(s1,c2,s3) __cat(s1,c2,s3)
+RedBoot_config_option(_cat("Boot script timeout (",
+                           CYGNUM_REDBOOT_BOOT_SCRIPT_TIMEOUT_RESOLUTION,
+                           "ms resolution)"),
+                      boot_script_timeout,
+                      "boot_script", true,
+                      CONFIG_INT,
+                      0
+    );
+#undef __cat
+#undef _cat
+
+#ifdef CYGSEM_REDBOOT_VARIABLE_BAUD_RATE
+RedBoot_config_option("Console baud rate",
+                      console_baud_rate,
+                      ALWAYS_ENABLED, true,
+                      CONFIG_INT,
+                      CYGNUM_HAL_VIRTUAL_VECTOR_CONSOLE_CHANNEL_BAUD
+    );
+#endif
+
+CYG_HAL_TABLE_BEGIN( __CONFIG_options_TAB__, RedBoot_config_options);
+CYG_HAL_TABLE_END( __CONFIG_options_TAB_END__, RedBoot_config_options);
+
+extern struct config_option __CONFIG_options_TAB__[], __CONFIG_options_TAB_END__[];
+
+// 
+// Layout of config data
+// Each data item is variable length, with the name, type and dependencies
+// encoded into the object.
+//  offset   contents
+//       0   data type
+//       1   length of name (N)
+//       2   enable sense
+//       3   length of enable key (M)
+//       4   key name
+//     N+4   enable key
+//   M+N+4   data value
+//
+
+#define CONFIG_OBJECT_TYPE(dp)          (dp)[0]
+#define CONFIG_OBJECT_KEYLEN(dp)        (dp)[1]
+#define CONFIG_OBJECT_ENABLE_SENSE(dp)  (dp)[2]
+#define CONFIG_OBJECT_ENABLE_KEYLEN(dp) (dp)[3]
+#define CONFIG_OBJECT_KEY(dp)           ((dp)+4)
+#define CONFIG_OBJECT_ENABLE_KEY(dp)    ((dp)+4+CONFIG_OBJECT_KEYLEN(dp))
+#define CONFIG_OBJECT_VALUE(dp)         ((dp)+4+CONFIG_OBJECT_KEYLEN(dp)+CONFIG_OBJECT_ENABLE_KEYLEN(dp))
+
+#define LIST_OPT_LIST_ONLY (1)
+#define LIST_OPT_NICKNAMES (2)
+#define LIST_OPT_FULLNAMES (4)
+#define LIST_OPT_DUMBTERM  (8)
+
+static void config_init(void);
+
+static int
+get_config(unsigned char *dp, char *title, int list_opt, char *newvalue )
+{
+    char line[256], hold_line[256], *sp, *lp;
+    int ret;
+    bool hold_bool_val, new_bool_val, enable;
+    unsigned long hold_int_val, new_int_val;
+#ifdef CYGPKG_REDBOOT_NETWORKING
+    in_addr_t hold_ip_val, new_ip_val;
+    enet_addr_t hold_esa_val;
+    int esa_ptr;
+    char *esp;
+#endif
+    void *val_ptr;
+    int type;
+
+    if (CONFIG_OBJECT_ENABLE_KEYLEN(dp)) {
+        flash_get_config(CONFIG_OBJECT_ENABLE_KEY(dp), &enable, CONFIG_BOOL);
+        if (((bool)CONFIG_OBJECT_ENABLE_SENSE(dp) && !enable) ||
+            (!(bool)CONFIG_OBJECT_ENABLE_SENSE(dp) && enable)) {
+            return CONFIG_OK;  // Disabled field
+        }
+    }
+    lp = line;  *lp = '\0';
+    val_ptr = (void *)CONFIG_OBJECT_VALUE(dp);
+    if (LIST_OPT_NICKNAMES & list_opt)
+        diag_printf("%s: ", CONFIG_OBJECT_KEY(dp));
+    if (LIST_OPT_FULLNAMES & list_opt) {
+        if (title != (char *)NULL) {
+            diag_printf("%s: ", title);
+        } else {
+            diag_printf("%s: ", CONFIG_OBJECT_KEY(dp));
+        }
+    }
+    switch (type = CONFIG_OBJECT_TYPE(dp)) {
+    case CONFIG_BOOL:
+        memcpy(&hold_bool_val, val_ptr, sizeof(bool));
+        lp += diag_sprintf(lp, "%s", hold_bool_val ? "true" : "false");
+        break;
+    case CONFIG_INT:
+        memcpy(&hold_int_val, val_ptr, sizeof(unsigned long));
+        lp += diag_sprintf(lp, "%ld", hold_int_val);
+        break;
+#ifdef CYGPKG_REDBOOT_NETWORKING
+    case CONFIG_IP:
+        lp += diag_sprintf(lp, "%s", inet_ntoa((in_addr_t *)val_ptr));
+        if (0 == strcmp("0.0.0.0", line) && !(LIST_OPT_LIST_ONLY & list_opt)) {
+            // then we have a deeply unhelpful starting text - kill it off
+            // (unless we are just listing all values)
+            lp = line;  *lp = '\0';
+        }
+        break;
+    case CONFIG_ESA:
+        for (esa_ptr = 0;  esa_ptr < sizeof(enet_addr_t);  esa_ptr++) {
+            lp += diag_sprintf(lp, "0x%02X", ((unsigned char *)val_ptr)[esa_ptr]);
+            if (esa_ptr < (sizeof(enet_addr_t)-1)) lp += diag_sprintf(lp, ":");
+        }
+        break;
+#if defined(CYGHWR_NET_DRIVERS) && (CYGHWR_NET_DRIVERS > 1)
+    case CONFIG_NETPORT:
+        lp += diag_sprintf(lp, "%s", (unsigned char *)val_ptr);
+        break;
+#endif
+#endif
+    case CONFIG_STRING:
+        lp += diag_sprintf(lp, "%s", (unsigned char *)val_ptr);
+        break;
+    case CONFIG_SCRIPT:
+        diag_printf("\n");
+        sp = lp = (unsigned char *)val_ptr;
+        while (*sp) {
+            while (*lp != '\n') lp++;
+            *lp = '\0';
+            diag_printf(".. %s\n", sp);
+            *lp++ = '\n';
+            sp = lp;
+        }
+        break;
+    }
+    if (LIST_OPT_LIST_ONLY & list_opt) {
+        diag_printf("%s\n", line);
+        return CONFIG_OK;
+    }
+    if (type != CONFIG_SCRIPT) {
+        if (NULL != newvalue) {
+            ret = strlen(newvalue);
+            if (ret > sizeof(line))
+                return CONFIG_BAD;
+            strcpy(hold_line, line); // Hold the old value for comparison
+            strcpy(line, newvalue);
+            diag_printf("Setting to %s\n", newvalue);
+        } else {
+            // read from terminal
+            strcpy(hold_line, line);
+            if (LIST_OPT_DUMBTERM & list_opt) {
+                diag_printf( (CONFIG_STRING == type ?
+                              "%s > " :
+                              "%s ? " ), line);
+                *line = '\0';
+            }
+            ret = _rb_gets_preloaded(line, sizeof(line), 0);
+        }
+        if (ret < 0) return CONFIG_ABORT;
+        // empty input - leave value untouched (else DNS goes away for a
+        // minute to try to look it up) but we must accept empty value for strings.
+        if (0 == line[0] && CONFIG_STRING != type) return CONFIG_OK; 
+        if (strcmp(line, hold_line) == 0) return CONFIG_OK;  // Just a CR - leave value untouched
+        lp = &line[strlen(line)-1];
+        if (*lp == '.') return CONFIG_DONE;
+        if (*lp == '^') return CONFIG_BACK;
+    }
+    switch (type) {
+    case CONFIG_BOOL:
+        memcpy(&hold_bool_val, val_ptr, sizeof(bool));
+        if (!parse_bool(line, &new_bool_val)) {
+            return CONFIG_BAD;
+        }
+        if (hold_bool_val != new_bool_val) {
+            memcpy(val_ptr, &new_bool_val, sizeof(bool));
+            return CONFIG_CHANGED;
+        } else {
+            return CONFIG_OK;
+        }
+        break;
+    case CONFIG_INT:
+        memcpy(&hold_int_val, val_ptr, sizeof(unsigned long));
+        if (!parse_num(line, &new_int_val, 0, 0)) {
+            return CONFIG_BAD;
+        }
+        if (hold_int_val != new_int_val) {
+            memcpy(val_ptr, &new_int_val, sizeof(unsigned long));
+            return CONFIG_CHANGED;
+        } else {
+            return CONFIG_OK;
+        }
+        break;
+#ifdef CYGPKG_REDBOOT_NETWORKING
+    case CONFIG_IP:
+        memcpy(&hold_ip_val.s_addr, &((in_addr_t *)val_ptr)->s_addr, sizeof(in_addr_t));
+        if (!_gethostbyname(line, &new_ip_val)) {
+            return CONFIG_BAD;
+        }
+        if (hold_ip_val.s_addr != new_ip_val.s_addr) {
+            memcpy(val_ptr, &new_ip_val, sizeof(in_addr_t));
+            return CONFIG_CHANGED;
+        } else {
+            return CONFIG_OK;
+        }
+        break;
+    case CONFIG_ESA:
+        memcpy(&hold_esa_val, val_ptr, sizeof(enet_addr_t));
+        esp = line;
+        for (esa_ptr = 0;  esa_ptr < sizeof(enet_addr_t);  esa_ptr++) {
+            unsigned long esa_byte;
+            if (!parse_num(esp, &esa_byte, &esp, ":")) {
+                memcpy(val_ptr, &hold_esa_val, sizeof(enet_addr_t));
+                return CONFIG_BAD;
+            }
+            ((unsigned char *)val_ptr)[esa_ptr] = esa_byte;
+        }
+        return CONFIG_CHANGED;
+        break;
+#if defined(CYGHWR_NET_DRIVERS) && (CYGHWR_NET_DRIVERS > 1)
+    case CONFIG_NETPORT:
+	if (strlen(line) >= MAX_STRING_LENGTH || net_devindex(line) < 0) {
+	    int index;
+	    const char *name;
+	    diag_printf("Sorry, Port name must be one of:\n");
+	    for (index = 0; (name = net_devname(index)) != NULL; index++)
+		diag_printf("    %s\n", name);
+            return CONFIG_BAD;
+	}
+        strcpy((unsigned char *)val_ptr, line);
+	break;
+#endif
+#endif
+    case CONFIG_SCRIPT:
+        // Assume it always changes
+        sp = (unsigned char *)val_ptr;
+        diag_printf("Enter script, terminate with empty line\n");
+        while (true) {
+            *sp = '\0';
+            diag_printf(">> ");
+            ret = _rb_gets(line, sizeof(line), 0);
+            if (ret < 0) return CONFIG_ABORT;
+            if (strlen(line) == 0) break;
+            lp = line;
+            while (*lp) {
+                *sp++ = *lp++;
+            }
+            *sp++ = '\n';
+        }
+        break;
+    case CONFIG_STRING:
+        if (strlen(line) >= MAX_STRING_LENGTH) {
+            diag_printf("Sorry, value is too long\n");
+            return CONFIG_BAD;
+        }
+        strcpy((unsigned char *)val_ptr, line);
+        break;
+    }
+    return CONFIG_CHANGED;
+}
+
+//
+// Manage configuration information with the FLASH
+//
+
+static int
+config_length(int type)
+{
+    switch (type) {
+    case CONFIG_BOOL:
+        return sizeof(bool);
+    case CONFIG_INT:
+        return sizeof(unsigned long);
+#ifdef CYGPKG_REDBOOT_NETWORKING
+    case CONFIG_IP:
+        return sizeof(in_addr_t);
+    case CONFIG_ESA:
+        // Would like this to be sizeof(enet_addr_t), but that causes much
+        // pain since it fouls the alignment of data which follows.
+        return 8;
+#if defined(CYGHWR_NET_DRIVERS) && (CYGHWR_NET_DRIVERS > 1)
+    case CONFIG_NETPORT:
+        return MAX_STRING_LENGTH;
+#endif
+#endif
+    case CONFIG_STRING:
+        return MAX_STRING_LENGTH;
+    case CONFIG_SCRIPT:
+        return MAX_SCRIPT_LENGTH;
+    default:
+        return 0;
+    }
+}
+
+static cmd_fun do_flash_config;
+RedBoot_cmd("fconfig",
+            "Manage configuration kept in FLASH memory",
+            "[-i] [-l] [-n] [-f] [-d] | [-d] nickname [value]",
+            do_flash_config
+    );
+
+static void
+do_flash_config(int argc, char *argv[])
+{
+    bool need_update = false;
+    struct config_option *optend = __CONFIG_options_TAB_END__;
+    struct config_option *opt = __CONFIG_options_TAB__;
+    struct option_info opts[5];
+    bool list_only;
+    bool nicknames;
+    bool fullnames;
+    bool dumbterminal;
+    int list_opt = 0;
+    unsigned char *dp;
+    int len, ret;
+    char *title;
+    char *onlyone = NULL;
+    char *onevalue = NULL;
+    bool doneone = false;
+    bool init = false;
+
+#ifdef CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH
+    if (!__flash_init) {
+        diag_printf("Sorry, no FLASH memory is available\n");
+        return;
+    }
+#endif
+    memcpy(backup_config, config, sizeof(struct _config));
+    script = (unsigned char *)0;
+
+    init_opts(&opts[0], 'l', false, OPTION_ARG_TYPE_FLG, 
+              (void **)&list_only, (bool *)0, "list configuration only");
+    init_opts(&opts[1], 'n', false, OPTION_ARG_TYPE_FLG, 
+              (void **)&nicknames, (bool *)0, "show nicknames");
+    init_opts(&opts[2], 'f', false, OPTION_ARG_TYPE_FLG, 
+              (void **)&fullnames, (bool *)0, "show full names");
+    init_opts(&opts[3], 'i', false, OPTION_ARG_TYPE_FLG, 
+              (void **)&init, (bool *)0, "initialize configuration database");
+    init_opts(&opts[4], 'd', false, OPTION_ARG_TYPE_FLG, 
+              (void **)&dumbterminal, (bool *)0, "dumb terminal: no clever edits");
+
+    // First look to see if we are setting or getting a single option
+    // by just quoting its nickname
+    if ( (2 == argc && '-' != argv[1][0]) ||
+         (3 == argc && '-' != argv[1][0] && '-' != argv[2][0])) {
+        // then the command was "fconfig foo [value]"
+        onlyone = argv[1];
+        onevalue = (3 == argc) ? argv[2] : NULL;
+        list_opt = LIST_OPT_NICKNAMES;
+    }
+    // Next see if we are setting or getting a single option with a dumb
+    // terminal invoked, ie. no line editing.
+    else if (3 == argc &&
+             '-' == argv[1][0] && 'd' == argv[1][1] && 0 == argv[1][2] && 
+             '-' != argv[2][0]) {
+        // then the command was "fconfig -d foo"
+        onlyone = argv[2];
+        onevalue = NULL;
+        list_opt = LIST_OPT_NICKNAMES | LIST_OPT_DUMBTERM;
+    }
+    else {
+        if (!scan_opts(argc, argv, 1, opts, 5, 0, 0, ""))
+            return;
+        list_opt |= list_only ? LIST_OPT_LIST_ONLY : 0;
+        list_opt |= nicknames ? LIST_OPT_NICKNAMES : LIST_OPT_FULLNAMES;
+        list_opt |= fullnames ? LIST_OPT_FULLNAMES : 0;
+        list_opt |= dumbterminal ? LIST_OPT_DUMBTERM : 0;
+    }
+
+    if (init && verify_action("Initialize non-volatile configuration")) {
+        config_init();
+        need_update = true;
+    }
+
+    dp = &config->config_data[0];
+    while (dp < &config->config_data[sizeof(config->config_data)]) {
+        if (CONFIG_OBJECT_TYPE(dp) == CONFIG_EMPTY) {
+            break;
+        }
+        len = 4 + CONFIG_OBJECT_KEYLEN(dp) + CONFIG_OBJECT_ENABLE_KEYLEN(dp) + 
+            config_length(CONFIG_OBJECT_TYPE(dp));
+        // Provide a title for well known [i.e. builtin] objects
+        title = (char *)NULL;
+        opt = __CONFIG_options_TAB__;
+        while (opt != optend) {
+            if (strcmp(opt->key, CONFIG_OBJECT_KEY(dp)) == 0) {
+                title = opt->title;
+                break;
+            }
+            opt++;
+        }
+        if ( onlyone && 0 != strcmp(CONFIG_OBJECT_KEY(dp), onlyone) )
+            ret = CONFIG_OK; // skip this entry
+        else {
+            doneone = true;
+            ret = get_config(dp, title, list_opt, onevalue); // do this opt
+        }
+        switch (ret) {
+        case CONFIG_DONE:
+            goto done;
+        case CONFIG_ABORT:
+            memcpy(config, backup_config, sizeof(struct _config));
+            return;
+        case CONFIG_CHANGED:
+            need_update = true;
+        case CONFIG_OK:
+            dp += len;
+            break;
+        case CONFIG_BACK:
+            dp = &config->config_data[0];
+            continue;
+        case CONFIG_BAD:
+            // Nothing - make him do it again
+            diag_printf ("** invalid entry\n");
+            onevalue = NULL; // request a good value be typed in - or abort/whatever
+        }
+    }
+
+ done:
+    if (NULL != onlyone && !doneone) {
+#ifdef CYGSEM_REDBOOT_ALLOW_DYNAMIC_FLASH_CONFIG_DATA
+        if (verify_action("** entry '%s' not found - add", onlyone)) {
+            struct config_option opt;
+            diag_printf("Trying to add value\n");
+        }
+#else
+        diag_printf("** entry '%s' not found", onlyone);
+#endif
+    }
+    if (!need_update)
+        return;
+    flash_write_config();
+}
+
+
+#ifdef CYGSEM_REDBOOT_FLASH_ALIASES
+static cmd_fun do_alias;
+RedBoot_cmd("alias",
+            "Manage aliases kept in FLASH memory",
+            "name [value]",
+            do_alias
+    );
+
+static void
+make_alias(char *alias, char *name)
+{
+    diag_sprintf(alias, "alias/%s", name);
+}
+
+static void
+do_alias(int argc, char *argv[])
+{
+    char name[80];
+    char *val;
+    struct config_option opt;
+
+    switch (argc) {
+    case 2:
+        make_alias(name, argv[1]);
+        if (flash_get_config(name, &val, CONFIG_STRING)) {
+            diag_printf("'%s' = '%s'\n", argv[1], val);
+        } else {
+            diag_printf("'%s' not found\n", argv[1]);
+        }
+        break;
+    case 3:
+        if (strlen(argv[2]) >= MAX_STRING_LENGTH) {
+            diag_printf("Sorry, value is too long\n");
+            break;
+        }
+        make_alias(name, argv[1]);
+        opt.type = CONFIG_STRING;
+        opt.enable = (char *)0;
+        opt.enable_sense = 1;
+        opt.key = name;
+        opt.dflt = (CYG_ADDRESS)argv[2];
+        flash_add_config(&opt, true);
+        break;
+    default:
+        diag_printf("usage: alias name [value]\n");
+    }
+}
+
+// Lookup an alias. First try plain string aliases. If that fails try
+// other types so allowing access to all configured values. This allows
+// for alias (macro) expansion of normal 'fconfig' data, such as the
+// board IP address.
+char *
+flash_lookup_alias(char *alias, char *alias_buf)
+{
+    char name[80];
+    char *val;
+    unsigned char * dp;
+    void *val_ptr;
+    int type;
+    bool hold_bool_val;
+    long hold_long_val;
+#ifdef CYGPKG_REDBOOT_NETWORKING
+    int esa_ptr;
+#endif
+
+    make_alias(name, alias);
+    if (flash_get_config(name, &val, CONFIG_STRING)) {
+        return val;
+    } else {
+        dp = flash_lookup_config(alias);
+        if (dp) {
+            val_ptr = (void *)CONFIG_OBJECT_VALUE(dp);
+            switch (type = CONFIG_OBJECT_TYPE(dp)) {
+            case CONFIG_BOOL:
+                memcpy(&hold_bool_val, val_ptr, sizeof(bool));
+                diag_sprintf(alias_buf, "%s", hold_bool_val ? "true" : "false");
+                break;
+            case CONFIG_INT:
+                memcpy(&hold_long_val, val_ptr, sizeof(unsigned long));
+                diag_sprintf(alias_buf,"%ld", hold_long_val);
+                break;
+#ifdef CYGPKG_REDBOOT_NETWORKING
+            case CONFIG_IP:
+                diag_sprintf(alias_buf,"%s", inet_ntoa((in_addr_t *)val_ptr));
+                break;
+            case CONFIG_ESA:
+                for (esa_ptr = 0;  esa_ptr < sizeof(enet_addr_t);  esa_ptr++) {
+                    diag_sprintf(alias_buf+(3*esa_ptr), "0x%02X", ((unsigned char *)val_ptr)[esa_ptr]);
+                    if (esa_ptr < (sizeof(enet_addr_t)-1)) diag_printf(":");
+                }
+                break;
+#endif
+            case CONFIG_SCRIPT:
+                return (char *) val_ptr;
+                break;
+            default:
+                return (char *)NULL;
+            }
+            return alias_buf;
+        } 
+        return (char *)NULL;
+    }
+}
+
+#endif //  CYGSEM_REDBOOT_FLASH_ALIASES
+
+//
+// Write the in-memory copy of the configuration data to the flash device.
+//
+void
+flash_write_config(void)
+{
+#if defined(CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG) && defined(CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH)
+    int stat;
+    void *err_addr;
+#endif
+
+    config->len = sizeof(struct _config);
+    config->key1 = CONFIG_KEY1;  
+    config->key2 = CONFIG_KEY2;
+    config->cksum = cyg_crc32((unsigned char *)config, sizeof(struct _config)-sizeof(config->cksum));
+    if (verify_action("Update RedBoot non-volatile configuration")) {
+#ifdef CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH
+#ifdef CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
+        memcpy(fis_work_block, fis_addr, fisdir_size);
+        fis_update_directory();
+#else //  CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
+#ifdef CYGSEM_REDBOOT_FLASH_LOCK_SPECIAL
+        // Insure [quietly] that the config page is unlocked before trying to update
+        flash_unlock((void *)cfg_base, cfg_size, (void **)&err_addr);
+#endif
+        if ((stat = flash_erase(cfg_base, cfg_size, (void **)&err_addr)) != 0) {
+            diag_printf("   initialization failed at %p: %s\n", err_addr, flash_errmsg(stat));
+        } else {
+            if ((stat = flash_program(cfg_base, (void *)config, sizeof(struct _config), 
+                                      (void **)&err_addr)) != 0) {
+                diag_printf("Error writing config data at %p: %s\n", 
+                            err_addr, flash_errmsg(stat));
+            }
+        }
+#ifdef CYGSEM_REDBOOT_FLASH_LOCK_SPECIAL
+        // Insure [quietly] that the config data is locked after the update
+        flash_lock((void *)cfg_base, cfg_size, (void **)&err_addr);
+#endif
+#endif
+#else
+        write_eeprom(config, sizeof(struct _config));  // into 'config'
+#endif
+    }
+}
+
+//
+// Find the configuration entry for a particular key
+//
+static unsigned char *
+flash_lookup_config(char *key)
+{
+    unsigned char *dp;
+    int len;
+
+    if (!config_ok) return (unsigned char *)NULL;
+
+    dp = &config->config_data[0];
+    while (dp < &config->config_data[sizeof(config->config_data)]) {
+        len = 4 + CONFIG_OBJECT_KEYLEN(dp) + CONFIG_OBJECT_ENABLE_KEYLEN(dp) +
+            config_length(CONFIG_OBJECT_TYPE(dp));
+        if (strcmp(key, CONFIG_OBJECT_KEY(dp)) == 0) {
+            return dp;
+        }
+        dp += len;
+    }
+//    diag_printf("Can't find config data for '%s'\n", key);
+    return false;
+}
+
+//
+// Retrieve a data object from the data base (in memory copy)
+//
+bool
+flash_get_config(char *key, void *val, int type)
+{
+    unsigned char *dp;
+    void *val_ptr;
+#ifdef CYGSEM_REDBOOT_FLASH_CONFIG_READONLY_FALLBACK
+    struct _config *save_config = 0;
+#endif
+
+    if (!config_ok) return false;
+
+    if ((dp = flash_lookup_config(key)) != (unsigned char *)NULL) {
+        if (CONFIG_OBJECT_TYPE(dp) == type) {
+            val_ptr = (void *)CONFIG_OBJECT_VALUE(dp);
+            switch (type) {
+                // Note: the data may be unaligned in the configuration data
+            case CONFIG_BOOL:
+                memcpy(val, val_ptr, sizeof(bool));
+                break;
+            case CONFIG_INT:
+                memcpy(val, val_ptr, sizeof(unsigned long));
+                break;
+#ifdef CYGPKG_REDBOOT_NETWORKING
+            case CONFIG_IP:
+                memcpy(val, val_ptr, sizeof(in_addr_t));
+                break;
+            case CONFIG_ESA:
+                memcpy(val, val_ptr, sizeof(enet_addr_t));
+                break;
+#endif
+#if defined(CYGHWR_NET_DRIVERS) && (CYGHWR_NET_DRIVERS > 1)
+	    case CONFIG_NETPORT:
+#endif
+            case CONFIG_STRING:
+            case CONFIG_SCRIPT:
+                // Just return a pointer to the script/line
+                *(unsigned char **)val = (unsigned char *)val_ptr;
+                break;
+            }
+        } else {
+            diag_printf("Request for config value '%s' - wrong type\n", key);
+        }
+        return true;
+    }
+#ifdef CYGSEM_REDBOOT_FLASH_CONFIG_READONLY_FALLBACK
+    // Did not find key. Is configuration data valid?
+    // Check to see if the config data is valid, if not, revert to 
+    // readonly mode, by setting config to readonly_config.  We
+    // will set it back before we leave this function.
+    if ( (config != readonly_config) && ((cyg_crc32((unsigned char *)config, 
+               sizeof(struct _config)-sizeof(config->cksum)) != config->cksum) ||
+        (config->key1 != CONFIG_KEY1)|| (config->key2 != CONFIG_KEY2))) {
+        save_config = config;
+        config = readonly_config;
+        if ((cyg_crc32((unsigned char *)config, 
+                       sizeof(struct _config)-sizeof(config->cksum)) != config->cksum) ||
+            (config->key1 != CONFIG_KEY1)|| (config->key2 != CONFIG_KEY2)) {
+            diag_printf("FLASH configuration checksum error or invalid key\n");
+            config = save_config;
+            return false;
+        }
+        else{
+            diag_printf("Getting config information in READONLY mode\n");
+            return flash_get_config(key, val, type);
+        }        
+    }
+#endif
+    return false;
+}
+
+//
+// Copy data into the config area
+//
+static void
+flash_config_insert_value(unsigned char *dp, struct config_option *opt)
+{
+    switch (opt->type) {
+        // Note: the data may be unaligned in the configuration data
+    case CONFIG_BOOL:
+        memcpy(dp, (void *)&opt->dflt, sizeof(bool));
+        break;
+    case CONFIG_INT:
+        memcpy(dp, (void *)&opt->dflt, sizeof(unsigned long));
+        break;
+#ifdef CYGPKG_REDBOOT_NETWORKING
+    case CONFIG_IP:
+        memcpy(dp, (void *)&opt->dflt, sizeof(in_addr_t));
+        break;
+    case CONFIG_ESA:
+        memcpy(dp, (void *)opt->dflt, sizeof(enet_addr_t));
+        break;
+#if defined(CYGHWR_NET_DRIVERS) && (CYGHWR_NET_DRIVERS > 1)
+    case CONFIG_NETPORT:
+	// validate dflt and if not acceptable use first port
+        {
+	    int index;
+	    const char *name;
+	    for (index = 0; (name = net_devname(index)) != NULL; index++)
+		if (!strcmp((char *)opt->dflt, name))
+		    break;
+	    if (name == NULL)
+		name = net_devname(0);
+	    memcpy(dp, name, strlen(name) + 1);
+        }
+        break;
+#endif
+#endif
+    case CONFIG_STRING:
+        memcpy(dp, (void *)opt->dflt, config_length(CONFIG_STRING));
+        break;
+    case CONFIG_SCRIPT:
+        break;
+    }
+}
+
+//
+// Add a new option to the database
+//
+bool
+flash_add_config(struct config_option *opt, bool update)
+{
+    unsigned char *dp, *kp;
+    int len, elen, size;
+
+    // If data item is already present, just update it
+    // Note: only the data value can be thusly changed
+    if ((dp = flash_lookup_config(opt->key)) != (unsigned char *)NULL) {
+        flash_config_insert_value(CONFIG_OBJECT_VALUE(dp), opt);
+        if (update) {
+            flash_write_config();
+        }
+        return true;
+    }
+    // Add the data item
+    dp = &config->config_data[0];
+    size = 0;
+    while (size < sizeof(config->config_data)) {
+        if (CONFIG_OBJECT_TYPE(dp) == CONFIG_EMPTY) {
+            kp = opt->key;
+            len = strlen(kp) + 1;
+            size += len + 2 + 2 + config_length(opt->type);
+            if (opt->enable) {
+                elen = strlen(opt->enable) + 1;
+                size += elen;
+            } else {
+                elen = 0;
+            }
+            if (size > sizeof(config->config_data)) {
+                break;
+            }
+            CONFIG_OBJECT_TYPE(dp) = opt->type; 
+            CONFIG_OBJECT_KEYLEN(dp) = len;
+            CONFIG_OBJECT_ENABLE_SENSE(dp) = opt->enable_sense;
+            CONFIG_OBJECT_ENABLE_KEYLEN(dp) = elen;
+            dp = CONFIG_OBJECT_KEY(dp);
+            while (*kp) *dp++ += *kp++;
+            *dp++ = '\0';    
+            if (elen) {
+                kp = opt->enable;
+                while (*kp) *dp++ += *kp++;
+                *dp++ = '\0';    
+            }
+            flash_config_insert_value(dp, opt);
+            if (update) {
+                flash_write_config();
+            }
+            return true;
+        } else {
+            len = 4 + CONFIG_OBJECT_KEYLEN(dp) + CONFIG_OBJECT_ENABLE_KEYLEN(dp) +
+                config_length(CONFIG_OBJECT_TYPE(dp));
+            dp += len;
+            size += len;
+        }
+    }
+    diag_printf("No space to add '%s'\n", opt->key);
+    return false;
+}
+
+//
+// Reset/initialize configuration data - used only when starting from scratch
+//
+static void
+config_init(void)
+{
+    // Well known option strings
+    struct config_option *optend = __CONFIG_options_TAB_END__;
+    struct config_option *opt = __CONFIG_options_TAB__;
+
+    memset(config, 0, sizeof(struct _config));
+    while (opt != optend) {
+        if (!flash_add_config(opt, false)) {
+            return;
+        }
+        opt++;
+    }
+    config_ok = true;
+}
+
+//
+// Attempt to get configuration information from the FLASH.
+// If available (i.e. good checksum, etc), initialize "known"
+// values for later use.
+//
+static void
+load_flash_config(void)
+{
+    bool use_boot_script;
+    void *err_addr;
+    unsigned char *cfg_temp = (unsigned char *)workspace_end;
+
+    config_ok = false;
+    script = (unsigned char *)0;
+    cfg_temp -= sizeof(struct _config);  // Space for primary config data
+    config = (struct _config *)cfg_temp;
+    cfg_temp -= sizeof(struct _config);  // Space for backup config data
+    backup_config = (struct _config *)cfg_temp;
+#ifdef CYGSEM_REDBOOT_FLASH_CONFIG_READONLY_FALLBACK
+    cfg_temp -= sizeof(struct _config);  // Space for readonly copy of config data
+    readonly_config = (struct _config *)cfg_temp;
+#endif
+    workspace_end = cfg_temp;
+#ifdef CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH
+    if (!do_flash_init()) return;
+    cfg_size = (flash_block_size > sizeof(struct _config)) ? 
+        sizeof(struct _config) : 
+        _rup(sizeof(struct _config), flash_block_size);
+#ifdef CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
+    cfg_size = _rup(cfg_size, sizeof(struct fis_image_desc));
+    if ((flash_block_size-cfg_size) < 8*sizeof(struct fis_image_desc)) {
+        // Too bad this can't be checked at compile/build time
+        diag_printf("Sorry, FLASH config exceeds available space in FIS directory\n");
+        return;
+    }
+    fisdir_size = flash_block_size - cfg_size;
+    cfg_base = (void *)(((CYG_ADDRESS)fis_addr + flash_block_size) - cfg_size);
+#else
+    if (CYGNUM_REDBOOT_FLASH_CONFIG_BLOCK < 0) {
+        cfg_base = (void *)((CYG_ADDRESS)flash_end + 1 -
+           _rup(_rup((-CYGNUM_REDBOOT_FLASH_CONFIG_BLOCK*flash_block_size), cfg_size), flash_block_size));
+    } else {
+        cfg_base = (void *)((CYG_ADDRESS)flash_start + 
+           _rup(_rup((CYGNUM_REDBOOT_FLASH_CONFIG_BLOCK*flash_block_size), cfg_size), flash_block_size));
+    }
+#endif
+    flash_read((void *)cfg_base, (void *)config, sizeof(struct _config), (void **)&err_addr);
+#else
+    read_eeprom(config, sizeof(struct _config));  // into 'config'
+#endif
+#ifdef CYGSEM_REDBOOT_FLASH_CONFIG_READONLY_FALLBACK
+    memcpy(readonly_config, config, sizeof(struct _config));
+#endif
+    if ((cyg_crc32((unsigned char *)config, 
+                   sizeof(struct _config)-sizeof(config->cksum)) != config->cksum) ||
+        (config->key1 != CONFIG_KEY1)|| (config->key2 != CONFIG_KEY2)) {
+        diag_printf("FLASH configuration checksum error or invalid key\n");
+        config_init();
+        return;
+    }
+    config_ok = true;
+    flash_get_config("boot_script", &use_boot_script, CONFIG_BOOL);
+    if (use_boot_script) {
+        flash_get_config("boot_script_data", &script, CONFIG_SCRIPT);
+        flash_get_config("boot_script_timeout", &script_timeout, CONFIG_INT);
+    }
+#ifdef CYGSEM_REDBOOT_VARIABLE_BAUD_RATE
+    if (flash_get_config("console_baud_rate", &console_baud_rate, CONFIG_INT)) {
+        extern int set_console_baud_rate(int);
+        set_console_baud_rate(console_baud_rate);
+    }
+#endif
+}
+
+RedBoot_init(load_flash_config, RedBoot_INIT_FIRST);
+
+// EOF fconfig.c