]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/redboot/v2_0/src/fconfig.c
92d85e6cc51f30b528a8edc38c59c68727a11b32
[karo-tx-redboot.git] / packages / redboot / v2_0 / src / fconfig.c
1 //==========================================================================
2 //
3 //      fconfig.c
4 //
5 //      RedBoot - persistent data storage support (FLASH or EEPROM)
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
12 // Copyright (C) 2003 Gary Thomas
13 //
14 // eCos is free software; you can redistribute it and/or modify it under
15 // the terms of the GNU General Public License as published by the Free
16 // Software Foundation; either version 2 or (at your option) any later version.
17 //
18 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21 // for more details.
22 //
23 // You should have received a copy of the GNU General Public License along
24 // with eCos; if not, write to the Free Software Foundation, Inc.,
25 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 //
27 // As a special exception, if other files instantiate templates or use macros
28 // or inline functions from this file, or you compile this file and link it
29 // with other works to produce a work based on this file, this file does not
30 // by itself cause the resulting work to be covered by the GNU General Public
31 // License. However the source code for this file must still be made available
32 // in accordance with section (3) of the GNU General Public License.
33 //
34 // This exception does not invalidate any other reasons why a work based on
35 // this file might be covered by the GNU General Public License.
36 //
37 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
38 // at http://sources.redhat.com/ecos/ecos-license/
39 // -------------------------------------------
40 //####ECOSGPLCOPYRIGHTEND####
41 //==========================================================================
42 //#####DESCRIPTIONBEGIN####
43 //
44 // Author(s):    gthomas
45 // Contributors: gthomas, tkoeller
46 // Date:         2000-07-28
47 // Purpose:      
48 // Description:  
49 //              
50 // This code is part of RedBoot (tm).
51 //
52 //####DESCRIPTIONEND####
53 //
54 //==========================================================================
55
56 #include <redboot.h>
57 #include <cyg/io/flash.h>
58 #ifdef CYGOPT_REDBOOT_FIS
59 #include <fis.h>
60 #endif
61
62 #ifdef CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
63 // Note horrid intertwining of functions, to save precious FLASH
64 externC void fis_read_directory(void);
65 externC void fis_update_directory(void);
66 #endif
67
68 #ifdef CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_EEPROM
69 externC void write_eeprom(void *buf, int len);
70 externC void read_eeprom(void *buf, int len);
71 #endif
72
73 #ifdef CYGSEM_REDBOOT_PLF_ESA_VALIDATE
74 externC bool cyg_plf_redboot_esa_validate(unsigned char *val);
75 #endif
76
77 #ifdef CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH
78 externC bool do_flash_init(void);
79 externC int flash_read(void *flash_base, void *ram_base, int len, void **err_address);
80 #endif
81
82 // Round a quantity up
83 #define _rup(n,s) ((((n)+(s-1))/s)*s)
84
85 #include <flash_config.h>
86
87 // Configuration data, saved in FLASH, used to set/update RedBoot
88 // normal "configuration" data items.
89 struct _config *config, *backup_config;
90
91 // Local data used by these routines
92 #ifdef CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH
93 extern void *flash_start, *flash_end;
94 extern int flash_block_size, flash_num_blocks;
95 extern int __flash_init;
96 #ifdef CYGOPT_REDBOOT_FIS
97 extern void *fis_work_block;
98 extern void *fis_addr;
99 extern int fisdir_size;  // Size of FIS directory.
100 #endif
101 #ifdef CYGSEM_REDBOOT_FLASH_CONFIG_READONLY_FALLBACK
102 static struct _config  *readonly_config;
103 #endif
104 void *cfg_base;   // Location in Flash of config data
105 int   cfg_size;   // Length of config data - rounded to Flash block size
106 #endif // FLASH MEDIA
107
108 // Prototypes for local functions
109 static unsigned char *flash_lookup_config(char *key);
110
111 static bool config_ok;
112
113 #define CONFIG_KEY1    0x0BADFACE
114 #define CONFIG_KEY2    0xDEADDEAD
115
116 #define CONFIG_DONE    0
117 #define CONFIG_ABORT  -1
118 #define CONFIG_CHANGED 1
119 #define CONFIG_OK      2
120 #define CONFIG_BACK    3
121 #define CONFIG_BAD     4
122
123 // Note: the following options are related.  If 'boot_script' is false, then
124 // the other values are used in the configuration.  Because of the way
125 // that configuration tables are generated, they should have names which
126 // are related.  The configuration options will show up lexicographically
127 // ordered, thus the peculiar naming.
128 RedBoot_config_option("Run script at boot",
129                       boot_script,
130                       ALWAYS_ENABLED, true,
131                       CONFIG_BOOL,
132                       false
133     );
134 RedBoot_config_option("Boot script",
135                       boot_script_data,
136                       "boot_script", true,
137                       CONFIG_SCRIPT,
138                       ""
139     );
140 // Some preprocessor magic for building the [constant] prompt string
141 #define __cat(s1,c2,s3) s1 #c2 s3
142 #define _cat(s1,c2,s3) __cat(s1,c2,s3)
143 RedBoot_config_option(_cat("Boot script timeout (",
144                            CYGNUM_REDBOOT_BOOT_SCRIPT_TIMEOUT_RESOLUTION,
145                            "ms resolution)"),
146                       boot_script_timeout,
147                       "boot_script", true,
148                       CONFIG_INT,
149                       0
150     );
151 #undef __cat
152 #undef _cat
153
154 #ifdef CYGSEM_REDBOOT_VARIABLE_BAUD_RATE
155 RedBoot_config_option("Console baud rate",
156                       console_baud_rate,
157                       ALWAYS_ENABLED, true,
158                       CONFIG_INT,
159                       CYGNUM_HAL_VIRTUAL_VECTOR_CONSOLE_CHANNEL_BAUD
160     );
161 #endif
162
163 CYG_HAL_TABLE_BEGIN( __CONFIG_options_TAB__, RedBoot_config_options);
164 CYG_HAL_TABLE_END( __CONFIG_options_TAB_END__, RedBoot_config_options);
165
166 extern struct config_option __CONFIG_options_TAB__[], __CONFIG_options_TAB_END__[];
167
168 // 
169 // Layout of config data
170 // Each data item is variable length, with the name, type and dependencies
171 // encoded into the object.
172 //  offset   contents
173 //       0   data type
174 //       1   length of name (N)
175 //       2   enable sense
176 //       3   length of enable key (M)
177 //       4   key name
178 //     N+4   enable key
179 //   M+N+4   data value
180 //
181
182 #define CONFIG_OBJECT_TYPE(dp)          (dp)[0]
183 #define CONFIG_OBJECT_KEYLEN(dp)        (dp)[1]
184 #define CONFIG_OBJECT_ENABLE_SENSE(dp)  (dp)[2]
185 #define CONFIG_OBJECT_ENABLE_KEYLEN(dp) (dp)[3]
186 #define CONFIG_OBJECT_KEY(dp)           ((dp)+4)
187 #define CONFIG_OBJECT_ENABLE_KEY(dp)    ((dp)+4+CONFIG_OBJECT_KEYLEN(dp))
188 #define CONFIG_OBJECT_VALUE(dp)         ((dp)+4+CONFIG_OBJECT_KEYLEN(dp)+CONFIG_OBJECT_ENABLE_KEYLEN(dp))
189
190 #define LIST_OPT_LIST_ONLY (1)
191 #define LIST_OPT_NICKNAMES (2)
192 #define LIST_OPT_FULLNAMES (4)
193 #define LIST_OPT_DUMBTERM  (8)
194
195 static void config_init(void);
196 static int  config_length(int type);
197
198 // Change endianness of config data
199 void
200 conf_endian_fixup(void *ptr)
201 {
202 #ifdef REDBOOT_FLASH_REVERSE_BYTEORDER
203     struct _config *p = (struct _config *)ptr;
204     unsigned char *dp = p->config_data;
205     void *val_ptr;
206     int len;
207     cyg_uint16 u16;
208     cyg_uint32 u32;
209
210     p->len = CYG_SWAP32(p->len);
211     p->key1 = CYG_SWAP32(p->key1);
212     p->key2 = CYG_SWAP32(p->key2);
213     p->cksum = CYG_SWAP32(p->cksum);
214
215     while (dp < &p->config_data[sizeof(config->config_data)]) {
216         len = 4 + CONFIG_OBJECT_KEYLEN(dp) + CONFIG_OBJECT_ENABLE_KEYLEN(dp) +
217             config_length(CONFIG_OBJECT_TYPE(dp));
218         val_ptr = (void *)CONFIG_OBJECT_VALUE(dp);
219
220         switch (CONFIG_OBJECT_TYPE(dp)) {
221             // Note: the data may be unaligned in the configuration data
222         case CONFIG_BOOL:
223             if (sizeof(bool) == 2) {
224                 memcpy(&u16, val_ptr, 2);
225                 u16 = CYG_SWAP16(u16);
226                 memcpy(val_ptr, &u16, 2);
227             } else if (sizeof(bool) == 4) {
228                 memcpy(&u32, val_ptr, 4);
229                 u32 = CYG_SWAP32(u32);
230                 memcpy(val_ptr, &u32, 4);
231             }
232             break;
233         case CONFIG_INT:
234             if (sizeof(unsigned long) == 2) {
235                 memcpy(&u16, val_ptr, 2);
236                 u16 = CYG_SWAP16(u16);
237                 memcpy(val_ptr, &u16, 2);
238             } else if (sizeof(unsigned long) == 4) {
239                 memcpy(&u32, val_ptr, 4);
240                 u32 = CYG_SWAP32(u32);
241                 memcpy(val_ptr, &u32, 4);
242             }
243             break;
244         }
245
246         dp += len;
247     }
248 #endif
249 }
250
251 static int
252 get_config(unsigned char *dp, char *title, int list_opt, char *newvalue )
253 {
254     char line[256], hold_line[256], *sp, *lp;
255     int ret;
256     bool hold_bool_val, new_bool_val, enable;
257     unsigned long hold_int_val, new_int_val;
258 #ifdef CYGPKG_REDBOOT_NETWORKING
259     in_addr_t hold_ip_val, new_ip_val;
260     enet_addr_t hold_esa_val;
261     int esa_ptr;
262     char *esp;
263 #endif
264     void *val_ptr;
265     int type, script_len;
266
267     if (CONFIG_OBJECT_ENABLE_KEYLEN(dp)) {
268         flash_get_config(CONFIG_OBJECT_ENABLE_KEY(dp), &enable, CONFIG_BOOL);
269         if (((bool)CONFIG_OBJECT_ENABLE_SENSE(dp) && !enable) ||
270             (!(bool)CONFIG_OBJECT_ENABLE_SENSE(dp) && enable)) {
271             return CONFIG_OK;  // Disabled field
272         }
273     }
274     lp = line;  *lp = '\0';
275     val_ptr = (void *)CONFIG_OBJECT_VALUE(dp);
276     if (LIST_OPT_NICKNAMES & list_opt)
277         diag_printf("%s: ", CONFIG_OBJECT_KEY(dp));
278     if (LIST_OPT_FULLNAMES & list_opt) {
279         if (title != (char *)NULL) {
280             diag_printf("%s: ", title);
281         } else {
282             diag_printf("%s: ", CONFIG_OBJECT_KEY(dp));
283         }
284     }
285     switch (type = CONFIG_OBJECT_TYPE(dp)) {
286     case CONFIG_BOOL:
287         memcpy(&hold_bool_val, val_ptr, sizeof(bool));
288         lp += diag_sprintf(lp, "%s", hold_bool_val ? "true" : "false");
289         break;
290     case CONFIG_INT:
291         memcpy(&hold_int_val, val_ptr, sizeof(unsigned long));
292         lp += diag_sprintf(lp, "%ld", hold_int_val);
293         break;
294 #ifdef CYGPKG_REDBOOT_NETWORKING
295     case CONFIG_IP:
296         lp += diag_sprintf(lp, "%s", inet_ntoa((in_addr_t *)val_ptr));
297         if (0 == strcmp("0.0.0.0", line) && !(LIST_OPT_LIST_ONLY & list_opt)) {
298             // then we have a deeply unhelpful starting text - kill it off
299             // (unless we are just listing all values)
300             lp = line;  *lp = '\0';
301         }
302         break;
303     case CONFIG_ESA:
304         for (esa_ptr = 0;  esa_ptr < sizeof(enet_addr_t);  esa_ptr++) {
305             lp += diag_sprintf(lp, "0x%02X", ((unsigned char *)val_ptr)[esa_ptr]);
306             if (esa_ptr < (sizeof(enet_addr_t)-1)) lp += diag_sprintf(lp, ":");
307         }
308         break;
309 #if defined(CYGHWR_NET_DRIVERS) && (CYGHWR_NET_DRIVERS > 1)
310     case CONFIG_NETPORT:
311         lp += diag_sprintf(lp, "%s", (unsigned char *)val_ptr);
312         break;
313 #endif
314 #endif
315     case CONFIG_STRING:
316         lp += diag_sprintf(lp, "%s", (unsigned char *)val_ptr);
317         break;
318     case CONFIG_SCRIPT:
319         diag_printf("\n");
320         sp = lp = (unsigned char *)val_ptr;
321         while (*sp) {
322             while (*lp != '\n') lp++;
323             *lp = '\0';
324             diag_printf(".. %s\n", sp);
325             *lp++ = '\n';
326             sp = lp;
327         }
328         break;
329     }
330     if (LIST_OPT_LIST_ONLY & list_opt) {
331         diag_printf("%s\n", line);
332         return CONFIG_OK;
333     }
334     if (type != CONFIG_SCRIPT) {
335         if (NULL != newvalue) {
336             ret = strlen(newvalue);
337             if (ret > sizeof(line))
338                 return CONFIG_BAD;
339             strcpy(hold_line, line); // Hold the old value for comparison
340             strcpy(line, newvalue);
341             diag_printf("Setting to %s\n", newvalue);
342         } else {
343             // read from terminal
344             strcpy(hold_line, line);
345             if (LIST_OPT_DUMBTERM & list_opt) {
346                 diag_printf( (CONFIG_STRING == type ?
347                               "%s > " :
348                               "%s ? " ), line);
349                 *line = '\0';
350             }
351             ret = _rb_gets_preloaded(line, sizeof(line), 0);
352         }
353         if (ret < 0) return CONFIG_ABORT;
354         // empty input - leave value untouched (else DNS goes away for a
355         // minute to try to look it up) but we must accept empty value for strings.
356         if (0 == line[0] && CONFIG_STRING != type) return CONFIG_OK; 
357         if (strcmp(line, hold_line) == 0) return CONFIG_OK;  // Just a CR - leave value untouched
358         lp = &line[strlen(line)-1];
359         if (*lp == '.') return CONFIG_DONE;
360         if (*lp == '^') return CONFIG_BACK;
361     }
362     switch (type) {
363     case CONFIG_BOOL:
364         memcpy(&hold_bool_val, val_ptr, sizeof(bool));
365         if (!parse_bool(line, &new_bool_val)) {
366             return CONFIG_BAD;
367         }
368         if (hold_bool_val != new_bool_val) {
369             memcpy(val_ptr, &new_bool_val, sizeof(bool));
370             return CONFIG_CHANGED;
371         } else {
372             return CONFIG_OK;
373         }
374         break;
375     case CONFIG_INT:
376         memcpy(&hold_int_val, val_ptr, sizeof(unsigned long));
377         if (!parse_num(line, &new_int_val, 0, 0)) {
378             return CONFIG_BAD;
379         }
380         if (hold_int_val != new_int_val) {
381             memcpy(val_ptr, &new_int_val, sizeof(unsigned long));
382             return CONFIG_CHANGED;
383         } else {
384             return CONFIG_OK;
385         }
386         break;
387 #ifdef CYGPKG_REDBOOT_NETWORKING
388     case CONFIG_IP:
389         memcpy(&hold_ip_val.s_addr, &((in_addr_t *)val_ptr)->s_addr, sizeof(in_addr_t));
390         if (!_gethostbyname(line, &new_ip_val)) {
391             return CONFIG_BAD;
392         }
393         if (hold_ip_val.s_addr != new_ip_val.s_addr) {
394             memcpy(val_ptr, &new_ip_val, sizeof(in_addr_t));
395             return CONFIG_CHANGED;
396         } else {
397             return CONFIG_OK;
398         }
399         break;
400     case CONFIG_ESA:
401         memcpy(&hold_esa_val, val_ptr, sizeof(enet_addr_t));
402         esp = line;
403         for (esa_ptr = 0;  esa_ptr < sizeof(enet_addr_t);  esa_ptr++) {
404             unsigned long esa_byte;
405             if (!parse_num(esp, &esa_byte, &esp, ":")) {
406                 memcpy(val_ptr, &hold_esa_val, sizeof(enet_addr_t));
407                 return CONFIG_BAD;
408             }
409             ((unsigned char *)val_ptr)[esa_ptr] = esa_byte;
410         }
411 #ifdef CYGSEM_REDBOOT_PLF_ESA_VALIDATE
412         if (!cyg_plf_redboot_esa_validate(val_ptr)) {
413             memcpy(val_ptr, &hold_esa_val, sizeof(enet_addr_t));
414             return CONFIG_BAD;
415         }
416 #endif
417         return CONFIG_CHANGED;
418         break;
419 #if defined(CYGHWR_NET_DRIVERS) && (CYGHWR_NET_DRIVERS > 1)
420     case CONFIG_NETPORT:
421         if (strlen(line) >= MAX_STRING_LENGTH || net_devindex(line) < 0) {
422             int index;
423             const char *name;
424             diag_printf("Sorry, Port name must be one of:\n");
425             for (index = 0; (name = net_devname(index)) != NULL; index++)
426                 diag_printf("    %s\n", name);
427             return CONFIG_BAD;
428         }
429         strcpy((unsigned char *)val_ptr, line);
430         break;
431 #endif
432 #endif
433     case CONFIG_SCRIPT:
434         // Assume it always changes
435         sp = (unsigned char *)val_ptr;
436         script_len = 0;
437         diag_printf("Enter script, terminate with empty line\n");
438         while (true) {
439             *sp = '\0';
440             diag_printf(">> ");
441             ret = _rb_gets(line, sizeof(line), 0);
442             if (ret < 0) return CONFIG_ABORT;
443             if (strlen(line) == 0) break;
444             script_len += strlen(line) + 1;
445             if (script_len > config_length(CONFIG_SCRIPT)) {
446                 diag_printf("script longer than %d not allowed!\n", 
447                                 config_length(CONFIG_SCRIPT));
448                 return CONFIG_ABORT;
449             }
450             lp = line;
451             while (*lp) {
452                 *sp++ = *lp++;
453             }
454             *sp++ = '\n';
455         }
456         break;
457     case CONFIG_STRING:
458         if (strlen(line) >= MAX_STRING_LENGTH) {
459             diag_printf("Sorry, value is too long\n");
460             return CONFIG_BAD;
461         }
462         strcpy((unsigned char *)val_ptr, line);
463         break;
464     }
465     return CONFIG_CHANGED;
466 }
467
468 //
469 // Manage configuration information with the FLASH
470 //
471
472 static int
473 config_length(int type)
474 {
475     switch (type) {
476     case CONFIG_BOOL:
477         return sizeof(bool);
478     case CONFIG_INT:
479         return sizeof(unsigned long);
480 #ifdef CYGPKG_REDBOOT_NETWORKING
481     case CONFIG_IP:
482         return sizeof(in_addr_t);
483     case CONFIG_ESA:
484         // Would like this to be sizeof(enet_addr_t), but that causes much
485         // pain since it fouls the alignment of data which follows.
486         return 8;
487 #if defined(CYGHWR_NET_DRIVERS) && (CYGHWR_NET_DRIVERS > 1)
488     case CONFIG_NETPORT:
489         return MAX_STRING_LENGTH;
490 #endif
491 #endif
492     case CONFIG_STRING:
493         return MAX_STRING_LENGTH;
494     case CONFIG_SCRIPT:
495         return MAX_SCRIPT_LENGTH;
496     default:
497         return 0;
498     }
499 }
500
501 static cmd_fun do_flash_config;
502 RedBoot_cmd("fconfig",
503             "Manage configuration kept in FLASH memory",
504             "[-i] [-l] [-n] [-f] [-d] | [-d] nickname [value]",
505             do_flash_config
506     );
507
508 static void
509 do_flash_config(int argc, char *argv[])
510 {
511     bool need_update = false;
512     struct config_option *optend = __CONFIG_options_TAB_END__;
513     struct config_option *opt = __CONFIG_options_TAB__;
514     struct option_info opts[5];
515     bool list_only;
516     bool nicknames;
517     bool fullnames;
518     bool dumbterminal;
519     int list_opt = 0;
520     unsigned char *dp;
521     int len, ret;
522     char *title;
523     char *onlyone = NULL;
524     char *onevalue = NULL;
525     bool doneone = false;
526     bool init = false;
527
528 #ifdef CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH
529     if (!__flash_init) {
530         diag_printf("Sorry, no FLASH memory is available\n");
531         return;
532     }
533 #endif
534     memcpy(backup_config, config, sizeof(struct _config));
535     script = (unsigned char *)0;
536
537     init_opts(&opts[0], 'l', false, OPTION_ARG_TYPE_FLG, 
538               (void *)&list_only, (bool *)0, "list configuration only");
539     init_opts(&opts[1], 'n', false, OPTION_ARG_TYPE_FLG, 
540               (void *)&nicknames, (bool *)0, "show nicknames");
541     init_opts(&opts[2], 'f', false, OPTION_ARG_TYPE_FLG, 
542               (void *)&fullnames, (bool *)0, "show full names");
543     init_opts(&opts[3], 'i', false, OPTION_ARG_TYPE_FLG, 
544               (void *)&init, (bool *)0, "initialize configuration database");
545     init_opts(&opts[4], 'd', false, OPTION_ARG_TYPE_FLG, 
546               (void *)&dumbterminal, (bool *)0, "dumb terminal: no clever edits");
547
548     // First look to see if we are setting or getting a single option
549     // by just quoting its nickname
550     if ( (2 == argc && '-' != argv[1][0]) ||
551          (3 == argc && '-' != argv[1][0] && '-' != argv[2][0])) {
552         // then the command was "fconfig foo [value]"
553         onlyone = argv[1];
554         onevalue = (3 == argc) ? argv[2] : NULL;
555         list_opt = LIST_OPT_NICKNAMES;
556     }
557     // Next see if we are setting or getting a single option with a dumb
558     // terminal invoked, ie. no line editing.
559     else if (3 == argc &&
560              '-' == argv[1][0] && 'd' == argv[1][1] && 0 == argv[1][2] && 
561              '-' != argv[2][0]) {
562         // then the command was "fconfig -d foo"
563         onlyone = argv[2];
564         onevalue = NULL;
565         list_opt = LIST_OPT_NICKNAMES | LIST_OPT_DUMBTERM;
566     }
567     else {
568         if (!scan_opts(argc, argv, 1, opts, 5, 0, 0, ""))
569             return;
570         list_opt |= list_only ? LIST_OPT_LIST_ONLY : 0;
571         list_opt |= nicknames ? LIST_OPT_NICKNAMES : LIST_OPT_FULLNAMES;
572         list_opt |= fullnames ? LIST_OPT_FULLNAMES : 0;
573         list_opt |= dumbterminal ? LIST_OPT_DUMBTERM : 0;
574     }
575
576     if (init && verify_action("Initialize non-volatile configuration")) {
577         config_init();
578         need_update = true;
579     }
580
581     dp = &config->config_data[0];
582     while (dp < &config->config_data[sizeof(config->config_data)]) {
583         if (CONFIG_OBJECT_TYPE(dp) == CONFIG_EMPTY) {
584             break;
585         }
586         len = 4 + CONFIG_OBJECT_KEYLEN(dp) + CONFIG_OBJECT_ENABLE_KEYLEN(dp) + 
587             config_length(CONFIG_OBJECT_TYPE(dp));
588         // Provide a title for well known [i.e. builtin] objects
589         title = (char *)NULL;
590         opt = __CONFIG_options_TAB__;
591         while (opt != optend) {
592             if (strcmp(opt->key, CONFIG_OBJECT_KEY(dp)) == 0) {
593                 title = opt->title;
594                 break;
595             }
596             opt++;
597         }
598         if ( onlyone && 0 != strcmp(CONFIG_OBJECT_KEY(dp), onlyone) )
599             ret = CONFIG_OK; // skip this entry
600         else {
601             doneone = true;
602             ret = get_config(dp, title, list_opt, onevalue); // do this opt
603         }
604         switch (ret) {
605         case CONFIG_DONE:
606             goto done;
607         case CONFIG_ABORT:
608             memcpy(config, backup_config, sizeof(struct _config));
609             return;
610         case CONFIG_CHANGED:
611             need_update = true;
612         case CONFIG_OK:
613             dp += len;
614             break;
615         case CONFIG_BACK:
616             dp = &config->config_data[0];
617             continue;
618         case CONFIG_BAD:
619             // Nothing - make him do it again
620             diag_printf ("** invalid entry\n");
621             onevalue = NULL; // request a good value be typed in - or abort/whatever
622         }
623     }
624
625  done:
626     if (NULL != onlyone && !doneone) {
627 #ifdef CYGSEM_REDBOOT_ALLOW_DYNAMIC_FLASH_CONFIG_DATA
628         if (verify_action("** entry '%s' not found - add", onlyone)) {
629             struct config_option opt;
630             diag_printf("Trying to add value\n");
631         }
632 #else
633         diag_printf("** entry '%s' not found\n", onlyone);
634 #endif
635     }
636     if (!need_update)
637         return;
638     flash_write_config(true);
639 }
640
641
642 #ifdef CYGSEM_REDBOOT_FLASH_ALIASES
643 static cmd_fun do_alias;
644 RedBoot_cmd("alias",
645             "Manage aliases kept in FLASH memory",
646             "name [value]",
647             do_alias
648     );
649
650 static void
651 make_alias(char *alias, char *name)
652 {
653     diag_sprintf(alias, "alias/%s", name);
654 }
655
656 static void
657 do_alias(int argc, char *argv[])
658 {
659     char name[80];
660     char *val;
661     struct config_option opt;
662
663     switch (argc) {
664     case 2:
665         make_alias(name, argv[1]);
666         if (flash_get_config(name, &val, CONFIG_STRING)) {
667             diag_printf("'%s' = '%s'\n", argv[1], val);
668         } else {
669             diag_printf("'%s' not found\n", argv[1]);
670         }
671         break;
672     case 3:
673         if (strlen(argv[2]) >= MAX_STRING_LENGTH) {
674             diag_printf("Sorry, value is too long\n");
675             break;
676         }
677         make_alias(name, argv[1]);
678         opt.type = CONFIG_STRING;
679         opt.enable = (char *)0;
680         opt.enable_sense = 1;
681         opt.key = name;
682         opt.dflt = (CYG_ADDRESS)argv[2];
683         flash_add_config(&opt, true);
684         break;
685     default:
686         diag_printf("usage: alias name [value]\n");
687     }
688 }
689
690 // Lookup an alias. First try plain string aliases. If that fails try
691 // other types so allowing access to all configured values. This allows
692 // for alias (macro) expansion of normal 'fconfig' data, such as the
693 // board IP address.
694 char *
695 flash_lookup_alias(char *alias, char *alias_buf)
696 {
697     char name[80];
698     char *val;
699     unsigned char * dp;
700     void *val_ptr;
701     int type;
702     bool hold_bool_val;
703     long hold_long_val;
704 #ifdef CYGPKG_REDBOOT_NETWORKING
705     int esa_ptr;
706 #endif
707
708     make_alias(name, alias);
709     if (flash_get_config(name, &val, CONFIG_STRING)) {
710         return val;
711     } else {
712         dp = flash_lookup_config(alias);
713         if (dp) {
714             val_ptr = (void *)CONFIG_OBJECT_VALUE(dp);
715             switch (type = CONFIG_OBJECT_TYPE(dp)) {
716             case CONFIG_BOOL:
717                 memcpy(&hold_bool_val, val_ptr, sizeof(bool));
718                 diag_sprintf(alias_buf, "%s", hold_bool_val ? "true" : "false");
719                 break;
720             case CONFIG_INT:
721                 memcpy(&hold_long_val, val_ptr, sizeof(unsigned long));
722                 diag_sprintf(alias_buf,"%ld", hold_long_val);
723                 break;
724 #ifdef CYGPKG_REDBOOT_NETWORKING
725             case CONFIG_IP:
726                 diag_sprintf(alias_buf,"%s", inet_ntoa((in_addr_t *)val_ptr));
727                 break;
728             case CONFIG_ESA:
729                 for (esa_ptr = 0;  esa_ptr < sizeof(enet_addr_t);  esa_ptr++) {
730                     diag_sprintf(alias_buf+(3*esa_ptr), "0x%02X", ((unsigned char *)val_ptr)[esa_ptr]);
731                     if (esa_ptr < (sizeof(enet_addr_t)-1)) diag_printf(":");
732                 }
733                 break;
734 #endif
735             case CONFIG_SCRIPT:
736                 return (char *) val_ptr;
737                 break;
738             default:
739                 return (char *)NULL;
740             }
741             return alias_buf;
742         } 
743         return (char *)NULL;
744     }
745 }
746
747 #endif //  CYGSEM_REDBOOT_FLASH_ALIASES
748
749 cyg_uint32
750 flash_crc(struct _config *conf)
751 {
752     cyg_uint32 crc;
753 #ifdef REDBOOT_FLASH_REVERSE_BYTEORDER
754     int        swabbed = 0;
755
756     if (conf->key1 == CONFIG_KEY1 && conf->key2 == CONFIG_KEY2) {
757         swabbed = 1;
758         conf_endian_fixup(conf);
759     }
760 #endif
761  
762     crc = cyg_crc32((unsigned char *)conf, sizeof(*conf)-sizeof(conf->cksum));
763
764 #ifdef REDBOOT_FLASH_REVERSE_BYTEORDER
765     if (swabbed)
766         conf_endian_fixup(conf);
767 #endif
768     return crc;
769 }
770
771 //
772 // Write the in-memory copy of the configuration data to the flash device.
773 //
774 void
775 flash_write_config(bool prompt)
776 {
777 #if defined(CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH)
778 #if !defined(CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG)
779     void *err_addr;
780     int stat;
781 #endif
782 #endif
783
784     config->len = sizeof(struct _config);
785     config->key1 = CONFIG_KEY1;  
786     config->key2 = CONFIG_KEY2;
787     config->cksum = flash_crc(config);
788     if (!prompt || verify_action("Update RedBoot non-volatile configuration")) {
789 #ifdef CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH
790 #ifdef CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
791         fis_read_directory();
792         fis_update_directory();
793 #else 
794 #ifdef CYGSEM_REDBOOT_FLASH_LOCK_SPECIAL
795         // Insure [quietly] that the config page is unlocked before trying to update
796         flash_unlock((void *)cfg_base, cfg_size, (void **)&err_addr);
797 #endif
798         if ((stat = flash_erase(cfg_base, cfg_size, (void **)&err_addr)) != 0) {
799             diag_printf("   initialization failed at %p: %s\n", err_addr, flash_errmsg(stat));
800         } else {
801             conf_endian_fixup(config);
802             if ((stat = FLASH_PROGRAM(cfg_base, config, sizeof(struct _config), (void **)&err_addr)) != 0) {
803                 diag_printf("Error writing config data at %p: %s\n", 
804                             err_addr, flash_errmsg(stat));
805             }
806             conf_endian_fixup(config);
807         }
808 #ifdef CYGSEM_REDBOOT_FLASH_LOCK_SPECIAL
809         // Insure [quietly] that the config data is locked after the update
810         flash_lock((void *)cfg_base, cfg_size, (void **)&err_addr);
811 #endif
812 #endif // CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
813 #else  // CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH
814         write_eeprom(config, sizeof(struct _config));  // into 'config'
815 #endif
816     }
817 }
818
819 //
820 // Find the configuration entry for a particular key
821 //
822 static unsigned char *
823 flash_lookup_config(char *key)
824 {
825     unsigned char *dp;
826     int len;
827
828     if (!config_ok) return (unsigned char *)NULL;
829
830     dp = &config->config_data[0];
831     while (dp < &config->config_data[sizeof(config->config_data)]) {
832         len = 4 + CONFIG_OBJECT_KEYLEN(dp) + CONFIG_OBJECT_ENABLE_KEYLEN(dp) +
833             config_length(CONFIG_OBJECT_TYPE(dp));
834         if (strcmp(key, CONFIG_OBJECT_KEY(dp)) == 0) {
835             return dp;
836         }
837         dp += len;
838     }
839 //    diag_printf("Can't find config data for '%s'\n", key);
840     return false;
841 }
842
843 //
844 // Enumerate the keys from the configuration
845 //
846 bool
847 flash_next_key(char *key, int keylen, int *type, int *offset)
848 {
849     unsigned char *dp;
850     int len;
851
852     if (!config_ok) return false;
853     if ((*offset < 0) || (*offset >= MAX_CONFIG_DATA)) return false;
854
855     dp = &config->config_data[*offset];
856     if ((*type = CONFIG_OBJECT_TYPE(dp)) == CONFIG_EMPTY) return false;
857     if ((len = CONFIG_OBJECT_KEYLEN(dp)) > keylen) return false;        
858     memcpy(key, CONFIG_OBJECT_KEY(dp), len);
859     *offset += 4 + CONFIG_OBJECT_KEYLEN(dp) + CONFIG_OBJECT_ENABLE_KEYLEN(dp) +
860         config_length(CONFIG_OBJECT_TYPE(dp));
861     return true;
862 }
863
864 //
865 // Retrieve a data object from the data base (in memory copy)
866 //
867 bool
868 flash_get_config(char *key, void *val, int type)
869 {
870     unsigned char *dp;
871     void *val_ptr;
872 #ifdef CYGSEM_REDBOOT_FLASH_CONFIG_READONLY_FALLBACK
873     struct _config *save_config = 0;
874     bool res;
875 #endif
876
877     if (!config_ok) return false;
878
879     if ((dp = flash_lookup_config(key)) != (unsigned char *)NULL) {
880         if (CONFIG_OBJECT_TYPE(dp) == type) {
881             val_ptr = (void *)CONFIG_OBJECT_VALUE(dp);
882             switch (type) {
883                 // Note: the data may be unaligned in the configuration data
884             case CONFIG_BOOL:
885                 memcpy(val, val_ptr, sizeof(bool));
886                 break;
887             case CONFIG_INT:
888                 memcpy(val, val_ptr, sizeof(unsigned long));
889                 break;
890 #ifdef CYGPKG_REDBOOT_NETWORKING
891             case CONFIG_IP:
892                 memcpy(val, val_ptr, sizeof(in_addr_t));
893                 break;
894             case CONFIG_ESA:
895                 memcpy(val, val_ptr, sizeof(enet_addr_t));
896                 break;
897 #endif
898 #if defined(CYGHWR_NET_DRIVERS) && (CYGHWR_NET_DRIVERS > 1)
899             case CONFIG_NETPORT:
900 #endif
901             case CONFIG_STRING:
902             case CONFIG_SCRIPT:
903                 // Just return a pointer to the script/line
904                 *(unsigned char **)val = (unsigned char *)val_ptr;
905                 break;
906             }
907         } else {
908             diag_printf("Request for config value '%s' - wrong type\n", key);
909         }
910         return true;
911     }
912 #ifdef CYGSEM_REDBOOT_FLASH_CONFIG_READONLY_FALLBACK
913     // Did not find key. Is configuration data valid?
914     // Check to see if the config data is valid, if not, revert to 
915     // readonly mode, by setting config to readonly_config.  We
916     // will set it back before we leave this function.
917     if ( (config != readonly_config) && ((flash_crc(config) != config->cksum) ||
918         (config->key1 != CONFIG_KEY1)|| (config->key2 != CONFIG_KEY2))) {
919         save_config = config;
920         config = readonly_config;
921         if ((flash_crc(config) != config->cksum) ||
922             (config->key1 != CONFIG_KEY1)|| (config->key2 != CONFIG_KEY2)) {
923             diag_printf("FLASH configuration checksum error or invalid key\n");
924             config = save_config;
925             return false;
926         }
927         else{
928             diag_printf("Getting config information in READONLY mode\n");
929             res = flash_get_config(key, val, type);
930             config = save_config;
931             return res;
932         }        
933     }
934 #endif
935     return false;
936 }
937
938 //
939 // Update a data object in the data base (in memory copy & backing store)
940 //
941 bool
942 flash_set_config(char *key, void *val, int type)
943 {
944     unsigned char *dp;
945     void *val_ptr;
946
947     if (!config_ok) return false;
948
949     if ((dp = flash_lookup_config(key)) != (unsigned char *)NULL) {
950         if (CONFIG_OBJECT_TYPE(dp) == type) {
951             val_ptr = (void *)CONFIG_OBJECT_VALUE(dp);
952             switch (type) {
953                 // Note: the data may be unaligned in the configuration data
954             case CONFIG_BOOL:
955                 memcpy(val_ptr, val, sizeof(bool));
956                 break;
957             case CONFIG_INT:
958                 memcpy(val_ptr, val, sizeof(unsigned long));
959                 break;
960 #ifdef CYGPKG_REDBOOT_NETWORKING
961             case CONFIG_IP:
962                 memcpy(val_ptr, val, sizeof(in_addr_t));
963                 break;
964             case CONFIG_ESA:
965                 memcpy(val_ptr, val, sizeof(enet_addr_t));
966                 break;
967 #endif
968 #if defined(CYGHWR_NET_DRIVERS) && (CYGHWR_NET_DRIVERS > 1)
969             case CONFIG_NETPORT:
970 #endif
971             case CONFIG_STRING:
972             case CONFIG_SCRIPT:
973                 memcpy(val_ptr, val, config_length(CONFIG_STRING));
974                 break;
975             }
976         } else {
977             diag_printf("Can't set config value '%s' - wrong type\n", key);
978             return false;
979         }
980         flash_write_config(false);
981         return true;
982     }
983     return false;
984 }
985
986 //
987 // Copy data into the config area
988 //
989 static void
990 flash_config_insert_value(unsigned char *dp, struct config_option *opt)
991 {
992     switch (opt->type) {
993         // Note: the data may be unaligned in the configuration data
994     case CONFIG_BOOL:
995         memcpy(dp, (void *)&opt->dflt, sizeof(bool));
996         break;
997     case CONFIG_INT:
998         memcpy(dp, (void *)&opt->dflt, sizeof(unsigned long));
999         break;
1000 #ifdef CYGPKG_REDBOOT_NETWORKING
1001     case CONFIG_IP:
1002         memcpy(dp, (void *)&opt->dflt, sizeof(in_addr_t));
1003         break;
1004     case CONFIG_ESA:
1005         memcpy(dp, (void *)opt->dflt, sizeof(enet_addr_t));
1006         break;
1007 #if defined(CYGHWR_NET_DRIVERS) && (CYGHWR_NET_DRIVERS > 1)
1008     case CONFIG_NETPORT:
1009         // validate dflt and if not acceptable use first port
1010         {
1011             int index;
1012             const char *name;
1013             for (index = 0; (name = net_devname(index)) != NULL; index++)
1014                 if (!strcmp((char *)opt->dflt, name))
1015                     break;
1016             if (name == NULL)
1017                 name = net_devname(0);
1018             memcpy(dp, name, strlen(name) + 1);
1019         }
1020         break;
1021 #endif
1022 #endif
1023     case CONFIG_STRING:
1024         memcpy(dp, (void *)opt->dflt, config_length(CONFIG_STRING));
1025         break;
1026     case CONFIG_SCRIPT:
1027         break;
1028     }
1029 }
1030
1031 //
1032 // Add a new option to the database
1033 //
1034 bool
1035 flash_add_config(struct config_option *opt, bool update)
1036 {
1037     unsigned char *dp, *kp;
1038     int len, elen, size;
1039
1040     // If data item is already present, just update it
1041     // Note: only the data value can be thusly changed
1042     if ((dp = flash_lookup_config(opt->key)) != (unsigned char *)NULL) {
1043         flash_config_insert_value(CONFIG_OBJECT_VALUE(dp), opt);
1044         if (update) {
1045             flash_write_config(true);
1046         }
1047         return true;
1048     }
1049     // Add the data item
1050     dp = &config->config_data[0];
1051     size = 0;
1052     while (size < sizeof(config->config_data)) {
1053         if (CONFIG_OBJECT_TYPE(dp) == CONFIG_EMPTY) {
1054             kp = opt->key;
1055             len = strlen(kp) + 1;
1056             size += len + 2 + 2 + config_length(opt->type);
1057             if (opt->enable) {
1058                 elen = strlen(opt->enable) + 1;
1059                 size += elen;
1060             } else {
1061                 elen = 0;
1062             }
1063             if (size > sizeof(config->config_data)) {
1064                 break;
1065             }
1066             CONFIG_OBJECT_TYPE(dp) = opt->type; 
1067             CONFIG_OBJECT_KEYLEN(dp) = len;
1068             CONFIG_OBJECT_ENABLE_SENSE(dp) = opt->enable_sense;
1069             CONFIG_OBJECT_ENABLE_KEYLEN(dp) = elen;
1070             dp = CONFIG_OBJECT_KEY(dp);
1071             while (*kp) *dp++ += *kp++;
1072             *dp++ = '\0';    
1073             if (elen) {
1074                 kp = opt->enable;
1075                 while (*kp) *dp++ += *kp++;
1076                 *dp++ = '\0';    
1077             }
1078             flash_config_insert_value(dp, opt);
1079             if (update) {
1080                 flash_write_config(true);
1081             }
1082             return true;
1083         } else {
1084             len = 4 + CONFIG_OBJECT_KEYLEN(dp) + CONFIG_OBJECT_ENABLE_KEYLEN(dp) +
1085                 config_length(CONFIG_OBJECT_TYPE(dp));
1086             dp += len;
1087             size += len;
1088         }
1089     }
1090     diag_printf("No space to add '%s'\n", opt->key);
1091     return false;
1092 }
1093
1094 //
1095 // Reset/initialize configuration data - used only when starting from scratch
1096 //
1097 static void
1098 config_init(void)
1099 {
1100     // Well known option strings
1101     struct config_option *optend = __CONFIG_options_TAB_END__;
1102     struct config_option *opt = __CONFIG_options_TAB__;
1103
1104     memset(config, 0, sizeof(struct _config));
1105     while (opt != optend) {
1106         if (!flash_add_config(opt, false)) {
1107             return;
1108         }
1109         opt++;
1110     }
1111     config_ok = true;
1112 }
1113
1114 //
1115 // Attempt to get configuration information from the FLASH.
1116 // If available (i.e. good checksum, etc), initialize "known"
1117 // values for later use.
1118 //
1119 static void
1120 load_flash_config(void)
1121 {
1122     bool use_boot_script;
1123     unsigned char *cfg_temp = (unsigned char *)workspace_end;
1124 #ifdef CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH
1125     void *err_addr;
1126 #endif
1127
1128     config_ok = false;
1129     script = (unsigned char *)0;
1130     cfg_temp -= sizeof(struct _config);  // Space for primary config data
1131     config = (struct _config *)cfg_temp;
1132     cfg_temp -= sizeof(struct _config);  // Space for backup config data
1133     backup_config = (struct _config *)cfg_temp;
1134 #ifdef CYGSEM_REDBOOT_FLASH_CONFIG_READONLY_FALLBACK
1135     cfg_temp -= sizeof(struct _config);  // Space for readonly copy of config data
1136     readonly_config = (struct _config *)cfg_temp;
1137 #endif
1138     workspace_end = cfg_temp;
1139 #ifdef CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH
1140     if (!do_flash_init()) return;
1141 #ifdef CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
1142     cfg_size = _rup(sizeof(struct _config), sizeof(struct fis_image_desc));
1143     if ((fisdir_size-cfg_size) < (CYGNUM_REDBOOT_FIS_DIRECTORY_ENTRY_COUNT *
1144                                   CYGNUM_REDBOOT_FIS_DIRECTORY_ENTRY_SIZE)) {
1145         // Too bad this can't be checked at compile/build time
1146         diag_printf("Sorry, FLASH config exceeds available space in FIS directory\n");
1147         return;
1148     }
1149     cfg_base = (void *)(((CYG_ADDRESS)fis_addr + fisdir_size) - cfg_size);
1150     fisdir_size -= cfg_size;
1151 #else
1152     cfg_size = (flash_block_size > sizeof(struct _config)) ? 
1153         sizeof(struct _config) : 
1154         _rup(sizeof(struct _config), flash_block_size);
1155     if (CYGNUM_REDBOOT_FLASH_CONFIG_BLOCK < 0) {
1156         cfg_base = (void *)((CYG_ADDRESS)flash_end + 1 -
1157            _rup(_rup((-CYGNUM_REDBOOT_FLASH_CONFIG_BLOCK*flash_block_size), cfg_size), flash_block_size));
1158     } else {
1159         cfg_base = (void *)((CYG_ADDRESS)flash_start + 
1160            _rup(_rup((CYGNUM_REDBOOT_FLASH_CONFIG_BLOCK*flash_block_size), cfg_size), flash_block_size));
1161     }
1162 #endif
1163     FLASH_READ(cfg_base, config, sizeof(struct _config), &err_addr);
1164     conf_endian_fixup(config);
1165 #else
1166     read_eeprom(config, sizeof(struct _config));  // into 'config'
1167 #endif
1168 #ifdef CYGSEM_REDBOOT_FLASH_CONFIG_READONLY_FALLBACK
1169     memcpy(readonly_config, config, sizeof(struct _config));
1170 #endif
1171     if ((flash_crc(config) != config->cksum) ||
1172         (config->key1 != CONFIG_KEY1)|| (config->key2 != CONFIG_KEY2)) {
1173         diag_printf("**Warning** FLASH configuration checksum error or invalid key\n");
1174         diag_printf("Use 'fconfig -i' to [re]initialize database\n");
1175         config_init();
1176         return;
1177     }
1178     config_ok = true;
1179     flash_get_config("boot_script", &use_boot_script, CONFIG_BOOL);
1180     if (use_boot_script) {
1181         flash_get_config("boot_script_data", &script, CONFIG_SCRIPT);
1182         flash_get_config("boot_script_timeout", &script_timeout, CONFIG_INT);
1183     }
1184 #ifdef CYGSEM_REDBOOT_VARIABLE_BAUD_RATE
1185     if (flash_get_config("console_baud_rate", &console_baud_rate, CONFIG_INT)) {
1186         extern int set_console_baud_rate(int);
1187         set_console_baud_rate(console_baud_rate);
1188     }
1189 #endif
1190 }
1191
1192 RedBoot_init(load_flash_config, RedBoot_INIT_SECOND);
1193
1194 // EOF fconfig.c