]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/redboot/v2_0/src/fconfig.c
unified MX27, MX25, MX37 trees
[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 int 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 = 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;
275     *lp = '\0';
276     val_ptr = CONFIG_OBJECT_VALUE(dp);
277     if (LIST_OPT_NICKNAMES & list_opt)
278         diag_printf("%s: ", CONFIG_OBJECT_KEY(dp));
279     if (LIST_OPT_FULLNAMES & list_opt) {
280         if (title != NULL) {
281             diag_printf("%s: ", title);
282         } else {
283             diag_printf("%s: ", CONFIG_OBJECT_KEY(dp));
284         }
285     }
286     switch (type = CONFIG_OBJECT_TYPE(dp)) {
287     case CONFIG_BOOL:
288         memcpy(&hold_bool_val, val_ptr, sizeof(bool));
289         lp += diag_sprintf(lp, "%s", hold_bool_val ? "true" : "false");
290         break;
291     case CONFIG_INT:
292         memcpy(&hold_int_val, val_ptr, sizeof(unsigned long));
293         lp += diag_sprintf(lp, "%ld", hold_int_val);
294         break;
295 #ifdef CYGPKG_REDBOOT_NETWORKING
296     case CONFIG_IP:
297         lp += diag_sprintf(lp, "%s", inet_ntoa((in_addr_t *)val_ptr));
298         if (0 == strcmp("0.0.0.0", line) && !(LIST_OPT_LIST_ONLY & list_opt)) {
299             // then we have a deeply unhelpful starting text - kill it off
300             // (unless we are just listing all values)
301             lp = line;  *lp = '\0';
302         }
303         break;
304     case CONFIG_ESA:
305         for (esa_ptr = 0;  esa_ptr < sizeof(enet_addr_t);  esa_ptr++) {
306             lp += diag_sprintf(lp, "0x%02X", ((unsigned char *)val_ptr)[esa_ptr]);
307             if (esa_ptr < (sizeof(enet_addr_t)-1)) lp += diag_sprintf(lp, ":");
308         }
309         break;
310 #if defined(CYGHWR_NET_DRIVERS) && (CYGHWR_NET_DRIVERS > 1)
311     case CONFIG_NETPORT:
312         lp += diag_sprintf(lp, "%s", (unsigned char *)val_ptr);
313         break;
314 #endif
315 #endif
316     case CONFIG_STRING:
317         lp += diag_sprintf(lp, "%s", (unsigned char *)val_ptr);
318         break;
319     case CONFIG_SCRIPT:
320         diag_printf("\n");
321         sp = lp = (unsigned char *)val_ptr;
322         while (*sp) {
323             while (*lp != '\n') lp++;
324             *lp = '\0';
325             diag_printf(".. %s\n", sp);
326             *lp++ = '\n';
327             sp = lp;
328         }
329         break;
330     }
331     if (LIST_OPT_LIST_ONLY & list_opt) {
332         diag_printf("%s\n", line);
333         return CONFIG_OK;
334     }
335     if (type != CONFIG_SCRIPT) {
336         if (NULL != newvalue) {
337             ret = strlen(newvalue);
338             if (ret > sizeof(line))
339                 return CONFIG_BAD;
340             strcpy(hold_line, line); // Hold the old value for comparison
341             strcpy(line, newvalue);
342             diag_printf("Setting to %s\n", newvalue);
343         } else {
344             // read from terminal
345             strcpy(hold_line, line);
346             if (LIST_OPT_DUMBTERM & list_opt) {
347                 diag_printf( (CONFIG_STRING == type ?
348                               "%s > " :
349                               "%s ? " ), line);
350                 *line = '\0';
351             }
352             ret = _rb_gets_preloaded(line, sizeof(line), 0);
353         }
354         if (ret < 0) return CONFIG_ABORT;
355         // empty input - leave value untouched (else DNS goes away for a
356         // minute to try to look it up) but we must accept empty value for strings.
357         if (0 == line[0] && CONFIG_STRING != type) return CONFIG_OK; 
358         if (strcmp(line, hold_line) == 0) return CONFIG_OK;  // Just a CR - leave value untouched
359         lp = &line[strlen(line)-1];
360         if (*lp == '.') return CONFIG_DONE;
361         if (*lp == '^') return CONFIG_BACK;
362     }
363     switch (type) {
364     case CONFIG_BOOL:
365         memcpy(&hold_bool_val, val_ptr, sizeof(bool));
366         if (!parse_bool(line, &new_bool_val)) {
367             return CONFIG_BAD;
368         }
369         if (hold_bool_val != new_bool_val) {
370             memcpy(val_ptr, &new_bool_val, sizeof(bool));
371             return CONFIG_CHANGED;
372         } else {
373             return CONFIG_OK;
374         }
375         break;
376     case CONFIG_INT:
377         memcpy(&hold_int_val, val_ptr, sizeof(unsigned long));
378         if (!parse_num(line, &new_int_val, 0, 0)) {
379             return CONFIG_BAD;
380         }
381         if (hold_int_val != new_int_val) {
382             memcpy(val_ptr, &new_int_val, sizeof(unsigned long));
383             return CONFIG_CHANGED;
384         } else {
385             return CONFIG_OK;
386         }
387         break;
388 #ifdef CYGPKG_REDBOOT_NETWORKING
389     case CONFIG_IP:
390         memcpy(&hold_ip_val.s_addr, &((in_addr_t *)val_ptr)->s_addr, sizeof(in_addr_t));
391         if (!_gethostbyname(line, &new_ip_val)) {
392             return CONFIG_BAD;
393         }
394         if (hold_ip_val.s_addr != new_ip_val.s_addr) {
395             memcpy(val_ptr, &new_ip_val, sizeof(in_addr_t));
396             return CONFIG_CHANGED;
397         } else {
398             return CONFIG_OK;
399         }
400         break;
401     case CONFIG_ESA:
402         memcpy(&hold_esa_val, val_ptr, sizeof(enet_addr_t));
403         esp = line;
404         for (esa_ptr = 0;  esa_ptr < sizeof(enet_addr_t);  esa_ptr++) {
405             unsigned long esa_byte;
406             if (!parse_num(esp, &esa_byte, &esp, ":")) {
407                 memcpy(val_ptr, &hold_esa_val, sizeof(enet_addr_t));
408                 return CONFIG_BAD;
409             }
410             ((unsigned char *)val_ptr)[esa_ptr] = esa_byte;
411         }
412 #ifdef CYGSEM_REDBOOT_PLF_ESA_VALIDATE
413         if (!cyg_plf_redboot_esa_validate(val_ptr)) {
414             memcpy(val_ptr, &hold_esa_val, sizeof(enet_addr_t));
415             return CONFIG_BAD;
416         }
417 #endif
418         return CONFIG_CHANGED;
419         break;
420 #if defined(CYGHWR_NET_DRIVERS) && (CYGHWR_NET_DRIVERS > 1)
421     case CONFIG_NETPORT:
422         if (strlen(line) >= MAX_STRING_LENGTH || net_devindex(line) < 0) {
423             int index;
424             const char *name;
425             diag_printf("Sorry, Port name must be one of:\n");
426             for (index = 0; (name = net_devname(index)) != NULL; index++)
427                 diag_printf("    %s\n", name);
428             return CONFIG_BAD;
429         }
430         strcpy((unsigned char *)val_ptr, line);
431         break;
432 #endif
433 #endif
434     case CONFIG_SCRIPT:
435         // Assume it always changes
436         sp = (unsigned char *)val_ptr;
437         script_len = 0;
438         diag_printf("Enter script, terminate with empty line\n");
439         while (true) {
440             *sp = '\0';
441             diag_printf(">> ");
442             ret = _rb_gets(line, sizeof(line), 0);
443             if (ret < 0) return CONFIG_ABORT;
444             if (strlen(line) == 0) break;
445             script_len += strlen(line) + 1;
446             if (script_len > config_length(CONFIG_SCRIPT)) {
447                 diag_printf("script longer than %d not allowed!\n", 
448                                 config_length(CONFIG_SCRIPT));
449                 return CONFIG_ABORT;
450             }
451             lp = line;
452             while (*lp) {
453                 *sp++ = *lp++;
454             }
455             *sp++ = '\n';
456         }
457         break;
458     case CONFIG_STRING:
459         if (strlen(line) >= MAX_STRING_LENGTH) {
460             diag_printf("Sorry, value is too long\n");
461             return CONFIG_BAD;
462         }
463         strcpy((unsigned char *)val_ptr, line);
464         break;
465     }
466     return CONFIG_CHANGED;
467 }
468
469 //
470 // Manage configuration information with the FLASH
471 //
472
473 static int
474 config_length(int type)
475 {
476     switch (type) {
477     case CONFIG_BOOL:
478         return sizeof(bool);
479     case CONFIG_INT:
480         return sizeof(unsigned long);
481 #ifdef CYGPKG_REDBOOT_NETWORKING
482     case CONFIG_IP:
483         return sizeof(in_addr_t);
484     case CONFIG_ESA:
485         // Would like this to be sizeof(enet_addr_t), but that causes much
486         // pain since it fouls the alignment of data which follows.
487         return 8;
488 #if defined(CYGHWR_NET_DRIVERS) && (CYGHWR_NET_DRIVERS > 1)
489     case CONFIG_NETPORT:
490         return MAX_STRING_LENGTH;
491 #endif
492 #endif
493     case CONFIG_STRING:
494         return MAX_STRING_LENGTH;
495     case CONFIG_SCRIPT:
496         return MAX_SCRIPT_LENGTH;
497     default:
498         return 0;
499     }
500 }
501
502 static cmd_fun do_flash_config;
503 RedBoot_cmd("fconfig",
504             "Manage configuration kept in FLASH memory",
505             "[-i] [-l] [-n] [-f] [-d] | [-d] nickname [value]",
506             do_flash_config
507     );
508
509 static void
510 do_flash_config(int argc, char *argv[])
511 {
512     bool need_update = false;
513     struct config_option *optend = __CONFIG_options_TAB_END__;
514     struct config_option *opt = __CONFIG_options_TAB__;
515     struct option_info opts[5];
516     bool list_only;
517     bool nicknames;
518     bool fullnames;
519     bool dumbterminal;
520     int list_opt = 0;
521     unsigned char *dp;
522     int len, ret;
523     char *title;
524     char *onlyone = NULL;
525     char *onevalue = NULL;
526     bool doneone = false;
527     bool init = false;
528
529 #ifdef CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH
530     if (!__flash_init) {
531         diag_printf("Sorry, no FLASH memory is available\n");
532         return;
533     }
534 #endif
535     memcpy(backup_config, config, sizeof(struct _config));
536     script = NULL;
537
538     init_opts(&opts[0], 'l', false, OPTION_ARG_TYPE_FLG, 
539               &list_only, NULL, "list configuration only");
540     init_opts(&opts[1], 'n', false, OPTION_ARG_TYPE_FLG, 
541               &nicknames, NULL, "show nicknames");
542     init_opts(&opts[2], 'f', false, OPTION_ARG_TYPE_FLG, 
543               &fullnames, NULL, "show full names");
544     init_opts(&opts[3], 'i', false, OPTION_ARG_TYPE_FLG, 
545               &init, NULL, "initialize configuration database");
546     init_opts(&opts[4], 'd', false, OPTION_ARG_TYPE_FLG, 
547               &dumbterminal, NULL, "dumb terminal: no clever edits");
548
549     // First look to see if we are setting or getting a single option
550     // by just quoting its nickname
551     if ( (2 == argc && '-' != argv[1][0]) ||
552          (3 == argc && '-' != argv[1][0] && '-' != argv[2][0])) {
553         // then the command was "fconfig foo [value]"
554         onlyone = argv[1];
555         onevalue = (3 == argc) ? argv[2] : NULL;
556         list_opt = LIST_OPT_NICKNAMES;
557     }
558     // Next see if we are setting or getting a single option with a dumb
559     // terminal invoked, ie. no line editing.
560     else if (3 == argc &&
561              '-' == argv[1][0] && 'd' == argv[1][1] && 0 == argv[1][2] && 
562              '-' != argv[2][0]) {
563         // then the command was "fconfig -d foo"
564         onlyone = argv[2];
565         onevalue = NULL;
566         list_opt = LIST_OPT_NICKNAMES | LIST_OPT_DUMBTERM;
567     }
568     else {
569         if (!scan_opts(argc, argv, 1, opts, 5, 0, 0, ""))
570             return;
571         list_opt |= list_only ? LIST_OPT_LIST_ONLY : 0;
572         list_opt |= nicknames ? LIST_OPT_NICKNAMES : LIST_OPT_FULLNAMES;
573         list_opt |= fullnames ? LIST_OPT_FULLNAMES : 0;
574         list_opt |= dumbterminal ? LIST_OPT_DUMBTERM : 0;
575     }
576
577     if (init && verify_action("Initialize non-volatile configuration")) {
578         config_init();
579         need_update = true;
580     }
581
582     dp = config->config_data;
583     while (dp < &config->config_data[sizeof(config->config_data)]) {
584         if (CONFIG_OBJECT_TYPE(dp) == CONFIG_EMPTY) {
585             break;
586         }
587         len = 4 + CONFIG_OBJECT_KEYLEN(dp) + CONFIG_OBJECT_ENABLE_KEYLEN(dp) + 
588                 config_length(CONFIG_OBJECT_TYPE(dp));
589         // Provide a title for well known [i.e. builtin] objects
590         title = NULL;
591         opt = __CONFIG_options_TAB__;
592         while (opt != optend) {
593             if (strcmp(opt->key, CONFIG_OBJECT_KEY(dp)) == 0) {
594                 title = opt->title;
595                 break;
596             }
597             opt++;
598         }
599         if (onlyone && 0 != strcmp(CONFIG_OBJECT_KEY(dp), onlyone))
600             ret = CONFIG_OK; // skip this entry
601         else {
602             doneone = true;
603             ret = get_config(dp, title, list_opt, onevalue); // do this opt
604         }
605         switch (ret) {
606         case CONFIG_DONE:
607             goto done;
608         case CONFIG_ABORT:
609             memcpy(config, backup_config, sizeof(struct _config));
610             return;
611         case CONFIG_CHANGED:
612             need_update = true;
613         case CONFIG_OK:
614             dp += len;
615             break;
616         case CONFIG_BACK:
617             dp = config->config_data;
618             continue;
619         case CONFIG_BAD:
620             // Nothing - make him do it again
621             diag_printf ("** invalid entry\n");
622             onevalue = NULL; // request a good value be typed in - or abort/whatever
623         }
624     }
625
626  done:
627     if (NULL != onlyone && !doneone) {
628 #ifdef CYGSEM_REDBOOT_ALLOW_DYNAMIC_FLASH_CONFIG_DATA
629         if (verify_action("** entry '%s' not found - add", onlyone)) {
630             struct config_option opt;
631             diag_printf("Trying to add value\n");
632         }
633 #else
634         diag_printf("** entry '%s' not found\n", onlyone);
635 #endif
636     }
637     if (!need_update)
638         return;
639     flash_write_config(true);
640 }
641
642
643 #ifdef CYGSEM_REDBOOT_FLASH_ALIASES
644 static cmd_fun do_alias;
645 RedBoot_cmd("alias",
646             "Manage aliases kept in FLASH memory",
647             "name [value]",
648             do_alias
649     );
650
651 static void
652 make_alias(char *alias, char *name)
653 {
654     diag_sprintf(alias, "alias/%s", name);
655 }
656
657 static void
658 do_alias(int argc, char *argv[])
659 {
660     char name[80];
661     char *val;
662     struct config_option opt;
663
664     switch (argc) {
665     case 2:
666         make_alias(name, argv[1]);
667         if (flash_get_config(name, &val, CONFIG_STRING)) {
668             diag_printf("'%s' = '%s'\n", argv[1], val);
669         } else {
670             diag_printf("'%s' not found\n", argv[1]);
671         }
672         break;
673     case 3:
674         if (strlen(argv[2]) >= MAX_STRING_LENGTH) {
675             diag_printf("Sorry, value is too long\n");
676             break;
677         }
678         make_alias(name, argv[1]);
679         opt.type = CONFIG_STRING;
680         opt.enable = NULL;
681         opt.enable_sense = 1;
682         opt.key = name;
683         opt.dflt = (CYG_ADDRESS)argv[2];
684         flash_add_config(&opt, true);
685         break;
686     default:
687         diag_printf("usage: alias name [value]\n");
688     }
689 }
690
691 // Lookup an alias. First try plain string aliases. If that fails try
692 // other types so allowing access to all configured values. This allows
693 // for alias (macro) expansion of normal 'fconfig' data, such as the
694 // board IP address.
695 char *
696 flash_lookup_alias(char *alias, char *alias_buf)
697 {
698     char name[80];
699     char *val;
700     unsigned char * dp;
701     void *val_ptr;
702     int type;
703     bool hold_bool_val;
704     long hold_long_val;
705 #ifdef CYGPKG_REDBOOT_NETWORKING
706     int esa_ptr;
707 #endif
708
709     make_alias(name, alias);
710     if (flash_get_config(name, &val, CONFIG_STRING)) {
711         return val;
712     } else {
713         dp = flash_lookup_config(alias);
714         if (dp) {
715             val_ptr = CONFIG_OBJECT_VALUE(dp);
716             switch (type = CONFIG_OBJECT_TYPE(dp)) {
717             case CONFIG_BOOL:
718                 memcpy(&hold_bool_val, val_ptr, sizeof(bool));
719                 diag_sprintf(alias_buf, "%s", hold_bool_val ? "true" : "false");
720                 break;
721             case CONFIG_INT:
722                 memcpy(&hold_long_val, val_ptr, sizeof(unsigned long));
723                 diag_sprintf(alias_buf,"%ld", hold_long_val);
724                 break;
725 #ifdef CYGPKG_REDBOOT_NETWORKING
726             case CONFIG_IP:
727                 diag_sprintf(alias_buf,"%s", inet_ntoa((in_addr_t *)val_ptr));
728                 break;
729             case CONFIG_ESA:
730                 for (esa_ptr = 0;  esa_ptr < sizeof(enet_addr_t);  esa_ptr++) {
731                     diag_sprintf(alias_buf+(3*esa_ptr), "0x%02X", ((unsigned char *)val_ptr)[esa_ptr]);
732                     if (esa_ptr < (sizeof(enet_addr_t)-1)) diag_printf(":");
733                 }
734                 break;
735 #endif
736             case CONFIG_SCRIPT:
737                 return val_ptr;
738                 break;
739             default:
740                 return NULL;
741             }
742             return alias_buf;
743         } 
744         return NULL;
745     }
746 }
747
748 #endif //  CYGSEM_REDBOOT_FLASH_ALIASES
749
750 cyg_uint32
751 flash_crc(struct _config *conf)
752 {
753     cyg_uint32 crc;
754 #ifdef REDBOOT_FLASH_REVERSE_BYTEORDER
755     int        swabbed = 0;
756
757     if (conf->key1 == CONFIG_KEY1 && conf->key2 == CONFIG_KEY2) {
758         swabbed = 1;
759         conf_endian_fixup(conf);
760     }
761 #endif
762  
763     crc = cyg_crc32((unsigned char *)conf, sizeof(*conf)-sizeof(conf->cksum));
764
765 #ifdef REDBOOT_FLASH_REVERSE_BYTEORDER
766     if (swabbed)
767         conf_endian_fixup(conf);
768 #endif
769     return crc;
770 }
771
772 //
773 // Write the in-memory copy of the configuration data to the flash device.
774 //
775 void
776 flash_write_config(bool prompt)
777 {
778 #if defined(CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH)
779 #if !defined(CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG)
780     void *err_addr;
781     int stat;
782 #endif
783 #endif
784
785     config->len = sizeof(struct _config);
786     config->key1 = CONFIG_KEY1;  
787     config->key2 = CONFIG_KEY2;
788     config->cksum = flash_crc(config);
789     if (!prompt || verify_action("Update RedBoot non-volatile configuration")) {
790 #ifdef CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH
791 #ifdef CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
792         fis_read_directory();
793         fis_update_directory();
794 #else 
795 #ifdef CYGSEM_REDBOOT_FLASH_LOCK_SPECIAL
796         // Ensure [quietly] that the config page is unlocked before trying to update
797         flash_unlock(cfg_base, cfg_size, &err_addr);
798 #endif
799         if ((stat = flash_erase(cfg_base, cfg_size, &err_addr)) != 0) {
800             diag_printf("   initialization failed at %p: %s\n", err_addr, flash_errmsg(stat));
801         } else {
802             conf_endian_fixup(config);
803             if ((stat = FLASH_PROGRAM(cfg_base, config, sizeof(struct _config), &err_addr)) != 0) {
804                 diag_printf("Error writing config data at %p: %s\n", 
805                             err_addr, flash_errmsg(stat));
806             }
807             conf_endian_fixup(config);
808         }
809 #ifdef CYGSEM_REDBOOT_FLASH_LOCK_SPECIAL
810         // Ensure [quietly] that the config data is locked after the update
811         flash_lock(cfg_base, cfg_size, &err_addr);
812 #endif
813 #endif // CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
814 #else  // CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH
815         write_eeprom(config, sizeof(struct _config));  // into 'config'
816 #endif
817     }
818 }
819
820 //
821 // Find the configuration entry for a particular key
822 //
823 static unsigned char *
824 flash_lookup_config(char *key)
825 {
826     unsigned char *dp;
827     int len;
828
829     if (!config_ok) return NULL;
830
831     dp = config->config_data;
832     while (dp < &config->config_data[sizeof(config->config_data)]) {
833         len = 4 + CONFIG_OBJECT_KEYLEN(dp) + CONFIG_OBJECT_ENABLE_KEYLEN(dp) +
834             config_length(CONFIG_OBJECT_TYPE(dp));
835         if (strcmp(key, CONFIG_OBJECT_KEY(dp)) == 0) {
836             return dp;
837         }
838         dp += len;
839     }
840 //    diag_printf("Can't find config data for '%s'\n", key);
841     return false;
842 }
843
844 //
845 // Enumerate the keys from the configuration
846 //
847 bool
848 flash_next_key(char *key, int keylen, int *type, int *offset)
849 {
850     unsigned char *dp;
851     int len;
852
853     if (!config_ok) return false;
854     if ((*offset < 0) || (*offset >= MAX_CONFIG_DATA)) return false;
855
856     dp = &config->config_data[*offset];
857     if ((*type = CONFIG_OBJECT_TYPE(dp)) == CONFIG_EMPTY) return false;
858     if ((len = CONFIG_OBJECT_KEYLEN(dp)) > keylen) return false;        
859     memcpy(key, CONFIG_OBJECT_KEY(dp), len);
860     *offset += 4 + CONFIG_OBJECT_KEYLEN(dp) + CONFIG_OBJECT_ENABLE_KEYLEN(dp) +
861         config_length(CONFIG_OBJECT_TYPE(dp));
862     return true;
863 }
864
865 //
866 // Retrieve a data object from the data base (in memory copy)
867 //
868 bool
869 flash_get_config(char *key, void *val, int type)
870 {
871     unsigned char *dp;
872     void *val_ptr;
873 #ifdef CYGSEM_REDBOOT_FLASH_CONFIG_READONLY_FALLBACK
874     struct _config *save_config = 0;
875     bool res;
876 #endif
877
878     if (!config_ok) return false;
879
880     if ((dp = flash_lookup_config(key)) != NULL) {
881         if (CONFIG_OBJECT_TYPE(dp) == type) {
882             val_ptr = CONFIG_OBJECT_VALUE(dp);
883             switch (type) {
884                 // Note: the data may be unaligned in the configuration data
885             case CONFIG_BOOL:
886                 memcpy(val, val_ptr, sizeof(bool));
887                 break;
888             case CONFIG_INT:
889                 memcpy(val, val_ptr, sizeof(unsigned long));
890                 break;
891 #ifdef CYGPKG_REDBOOT_NETWORKING
892             case CONFIG_IP:
893                 memcpy(val, val_ptr, sizeof(in_addr_t));
894                 break;
895             case CONFIG_ESA:
896                 memcpy(val, val_ptr, sizeof(enet_addr_t));
897                 break;
898 #endif
899 #if defined(CYGHWR_NET_DRIVERS) && (CYGHWR_NET_DRIVERS > 1)
900             case CONFIG_NETPORT:
901 #endif
902             case CONFIG_STRING:
903             case CONFIG_SCRIPT:
904                 // Just return a pointer to the script/line
905                 *(unsigned char **)val = (unsigned char *)val_ptr;
906                 break;
907             }
908         } else {
909             diag_printf("Request for config value '%s' - wrong type\n", key);
910         }
911         return true;
912     }
913 #ifdef CYGSEM_REDBOOT_FLASH_CONFIG_READONLY_FALLBACK
914     // Did not find key. Is configuration data valid?
915     // Check to see if the config data is valid, if not, revert to 
916     // readonly mode, by setting config to readonly_config.  We
917     // will set it back before we leave this function.
918     if ( (config != readonly_config) && ((flash_crc(config) != config->cksum) ||
919         (config->key1 != CONFIG_KEY1)|| (config->key2 != CONFIG_KEY2))) {
920         save_config = config;
921         config = readonly_config;
922         if ((flash_crc(config) != config->cksum) ||
923             (config->key1 != CONFIG_KEY1)|| (config->key2 != CONFIG_KEY2)) {
924             diag_printf("FLASH configuration checksum error or invalid key\n");
925             config = save_config;
926             return false;
927         }
928         else{
929             diag_printf("Getting config information in READONLY mode\n");
930             res = flash_get_config(key, val, type);
931             config = save_config;
932             return res;
933         }        
934     }
935 #endif
936     return false;
937 }
938
939 //
940 // Update a data object in the data base (in memory copy & backing store)
941 //
942 bool
943 flash_set_config(char *key, void *val, int type)
944 {
945     unsigned char *dp;
946     void *val_ptr;
947
948     if (!config_ok) return false;
949
950     if ((dp = flash_lookup_config(key)) != NULL) {
951         if (CONFIG_OBJECT_TYPE(dp) == type) {
952             val_ptr = CONFIG_OBJECT_VALUE(dp);
953             switch (type) {
954                 // Note: the data may be unaligned in the configuration data
955             case CONFIG_BOOL:
956                 memcpy(val_ptr, val, sizeof(bool));
957                 break;
958             case CONFIG_INT:
959                 memcpy(val_ptr, val, sizeof(unsigned long));
960                 break;
961 #ifdef CYGPKG_REDBOOT_NETWORKING
962             case CONFIG_IP:
963                 memcpy(val_ptr, val, sizeof(in_addr_t));
964                 break;
965             case CONFIG_ESA:
966                 memcpy(val_ptr, val, sizeof(enet_addr_t));
967                 break;
968 #endif
969 #if defined(CYGHWR_NET_DRIVERS) && (CYGHWR_NET_DRIVERS > 1)
970             case CONFIG_NETPORT:
971 #endif
972             case CONFIG_STRING:
973             case CONFIG_SCRIPT:
974                 memcpy(val_ptr, val, config_length(CONFIG_STRING));
975                 break;
976             }
977         } else {
978             diag_printf("Can't set config value '%s' - wrong type\n", key);
979             return false;
980         }
981         flash_write_config(false);
982         return true;
983     }
984     return false;
985 }
986
987 //
988 // Copy data into the config area
989 //
990 static void
991 flash_config_insert_value(unsigned char *dp, struct config_option *opt)
992 {
993     switch (opt->type) {
994         // Note: the data may be unaligned in the configuration data
995     case CONFIG_BOOL:
996         memcpy(dp, &opt->dflt, sizeof(bool));
997         break;
998     case CONFIG_INT:
999         memcpy(dp, &opt->dflt, sizeof(unsigned long));
1000         break;
1001 #ifdef CYGPKG_REDBOOT_NETWORKING
1002     case CONFIG_IP:
1003         memcpy(dp, &opt->dflt, sizeof(in_addr_t));
1004         break;
1005     case CONFIG_ESA:
1006         memcpy(dp, (void *)opt->dflt, sizeof(enet_addr_t));
1007         break;
1008 #if defined(CYGHWR_NET_DRIVERS) && (CYGHWR_NET_DRIVERS > 1)
1009     case CONFIG_NETPORT:
1010         // validate dflt and if not acceptable use first port
1011         {
1012             int index;
1013             const char *name;
1014             for (index = 0; (name = net_devname(index)) != NULL; index++)
1015                 if (!strcmp((char *)opt->dflt, name))
1016                     break;
1017             if (name == NULL)
1018                 name = net_devname(0);
1019             memcpy(dp, name, strlen(name) + 1);
1020         }
1021         break;
1022 #endif
1023 #endif
1024     case CONFIG_STRING:
1025         memcpy(dp, (void *)opt->dflt, config_length(CONFIG_STRING));
1026         break;
1027     case CONFIG_SCRIPT:
1028         break;
1029     }
1030 }
1031
1032 //
1033 // Add a new option to the database
1034 //
1035 bool
1036 flash_add_config(struct config_option *opt, bool update)
1037 {
1038     unsigned char *dp, *kp;
1039     int len, elen, size;
1040
1041     // If data item is already present, just update it
1042     // Note: only the data value can be thusly changed
1043     if ((dp = flash_lookup_config(opt->key)) != NULL) {
1044         flash_config_insert_value(CONFIG_OBJECT_VALUE(dp), opt);
1045         if (update) {
1046             flash_write_config(true);
1047         }
1048         return true;
1049     }
1050     // Add the data item
1051     dp = config->config_data;
1052     size = 0;
1053     while (size < sizeof(config->config_data)) {
1054         if (CONFIG_OBJECT_TYPE(dp) == CONFIG_EMPTY) {
1055             kp = opt->key;
1056             len = strlen(kp) + 1;
1057             size += len + 2 + 2 + config_length(opt->type);
1058             if (opt->enable) {
1059                 elen = strlen(opt->enable) + 1;
1060                 size += elen;
1061             } else {
1062                 elen = 0;
1063             }
1064             if (size > sizeof(config->config_data)) {
1065                 break;
1066             }
1067             CONFIG_OBJECT_TYPE(dp) = opt->type; 
1068             CONFIG_OBJECT_KEYLEN(dp) = len;
1069             CONFIG_OBJECT_ENABLE_SENSE(dp) = opt->enable_sense;
1070             CONFIG_OBJECT_ENABLE_KEYLEN(dp) = elen;
1071             dp = CONFIG_OBJECT_KEY(dp);
1072             while (*kp) *dp++ += *kp++;
1073             *dp++ = '\0';    
1074             if (elen) {
1075                 kp = opt->enable;
1076                 while (*kp) *dp++ += *kp++;
1077                 *dp++ = '\0';    
1078             }
1079             flash_config_insert_value(dp, opt);
1080             if (update) {
1081                 flash_write_config(true);
1082             }
1083             return true;
1084         } else {
1085             len = 4 + CONFIG_OBJECT_KEYLEN(dp) + CONFIG_OBJECT_ENABLE_KEYLEN(dp) +
1086                 config_length(CONFIG_OBJECT_TYPE(dp));
1087             dp += len;
1088             size += len;
1089         }
1090     }
1091     diag_printf("No space to add '%s'\n", opt->key);
1092     return false;
1093 }
1094
1095 //
1096 // Reset/initialize configuration data - used only when starting from scratch
1097 //
1098 static void
1099 config_init(void)
1100 {
1101     // Well known option strings
1102     struct config_option *optend = __CONFIG_options_TAB_END__;
1103     struct config_option *opt = __CONFIG_options_TAB__;
1104
1105     memset(config, 0, sizeof(struct _config));
1106     while (opt != optend) {
1107         if (!flash_add_config(opt, false)) {
1108             return;
1109         }
1110         opt++;
1111     }
1112     config_ok = true;
1113 }
1114
1115 //
1116 // Attempt to get configuration information from the FLASH.
1117 // If available (i.e. good checksum, etc), initialize "known"
1118 // values for later use.
1119 //
1120 static void
1121 load_flash_config(void)
1122 {
1123     bool use_boot_script;
1124     unsigned char *cfg_temp = workspace_end;
1125 #ifdef CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH
1126     void *err_addr;
1127 #endif
1128
1129     config_ok = false;
1130     script = NULL;
1131     cfg_temp -= sizeof(struct _config);  // Space for primary config data
1132     config = (struct _config *)cfg_temp;
1133     cfg_temp -= sizeof(struct _config);  // Space for backup config data
1134     backup_config = (struct _config *)cfg_temp;
1135 #ifdef CYGSEM_REDBOOT_FLASH_CONFIG_READONLY_FALLBACK
1136     cfg_temp -= sizeof(struct _config);  // Space for readonly copy of config data
1137     readonly_config = (struct _config *)cfg_temp;
1138 #endif
1139     workspace_end = cfg_temp;
1140 #ifdef CYGHWR_REDBOOT_FLASH_CONFIG_MEDIA_FLASH
1141     if (do_flash_init()<0) return;
1142 #ifdef CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
1143     cfg_size = _rup(sizeof(struct _config), sizeof(struct fis_image_desc));
1144     if ((fisdir_size-cfg_size) < (CYGNUM_REDBOOT_FIS_DIRECTORY_ENTRY_COUNT *
1145                                   CYGNUM_REDBOOT_FIS_DIRECTORY_ENTRY_SIZE)) {
1146         // Too bad this can't be checked at compile/build time
1147         diag_printf("Sorry, FLASH config exceeds available space in FIS directory\n");
1148         return;
1149     }
1150     cfg_base = (void *)(((CYG_ADDRESS)fis_addr + fisdir_size) - cfg_size);
1151     fisdir_size -= cfg_size;
1152 #else
1153     cfg_size = (flash_block_size > sizeof(struct _config)) ? 
1154         sizeof(struct _config) : 
1155         _rup(sizeof(struct _config), flash_block_size);
1156     if (CYGNUM_REDBOOT_FLASH_CONFIG_BLOCK < 0) {
1157         cfg_base = (void *)((CYG_ADDRESS)flash_end + 1 -
1158            _rup(_rup((-CYGNUM_REDBOOT_FLASH_CONFIG_BLOCK * flash_block_size), cfg_size),
1159                 flash_block_size));
1160     } else {
1161         cfg_base = (void *)((CYG_ADDRESS)flash_start + 
1162            _rup(_rup((CYGNUM_REDBOOT_FLASH_CONFIG_BLOCK * flash_block_size), cfg_size),
1163                 flash_block_size));
1164     }
1165 #endif
1166     FLASH_READ(cfg_base, config, sizeof(struct _config), &err_addr);
1167     conf_endian_fixup(config);
1168 #else
1169     read_eeprom(config, sizeof(struct _config));  // into 'config'
1170 #endif
1171 #ifdef CYGSEM_REDBOOT_FLASH_CONFIG_READONLY_FALLBACK
1172     memcpy(readonly_config, config, sizeof(struct _config));
1173 #endif
1174     if ((flash_crc(config) != config->cksum) ||
1175         (config->key1 != CONFIG_KEY1)|| (config->key2 != CONFIG_KEY2)) {
1176         diag_printf("**Warning** FLASH configuration checksum error or invalid key\n");
1177         diag_printf("Use 'fconfig -i' to [re]initialize database\n");
1178         config_init();
1179         return;
1180     }
1181     config_ok = true;
1182     flash_get_config("boot_script", &use_boot_script, CONFIG_BOOL);
1183     if (use_boot_script) {
1184         flash_get_config("boot_script_data", &script, CONFIG_SCRIPT);
1185         flash_get_config("boot_script_timeout", &script_timeout, CONFIG_INT);
1186     }
1187 #ifdef CYGSEM_REDBOOT_VARIABLE_BAUD_RATE
1188     if (flash_get_config("console_baud_rate", &console_baud_rate, CONFIG_INT)) {
1189         extern int set_console_baud_rate(int);
1190         set_console_baud_rate(console_baud_rate);
1191     }
1192 #endif
1193 }
1194
1195 RedBoot_init(load_flash_config, RedBoot_INIT_SECOND);
1196
1197 // EOF fconfig.c