// // $Id: env.c,v 1.10 2000/08/30 19:06:32 maurie Exp $ // // Copyright Motorola, Inc. 1997, 1999 ALL RIGHTS RESERVED // // You are hereby granted a copyright license to use, modify, and distribute the // SOFTWARE so long as this entire notice is retained without alteration in any // modified and/or redistributed versions, and that such modified versions are // clearly identified as such. No licenses are granted by implication, estoppel // or otherwise under any patents or trademarks of Motorola, Inc. // // The SOFTWARE is provided on an "AS IS" basis and without warranty. To the // maximum extent permitted by applicable law, MOTOROLA DISCLAIMS ALL WARRANTIES // WHETHER EXPRESS OR IMPLIED, INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY // OR FITNESS FOR A PARTICULAR PURPOSE AND ANY WARRANTY AGAINST INFRINGEMENT // WITH REGARD TO THE SOFTWARE (INCLUDING ANY MODIFIED VERSIONS THEREOF) AND ANY // ACCOMPANYING WRITTEN MATERIALS. // // To the maximum extent permitted by applicable law, IN NO EVENT SHALL MOTOROLA // BE LIABLE FOR ANY DAMAGES WHATSOEVER (INCLUDING WITHOUT LIMITATION, DAMAGES // FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS // INFORMATION, OR OTHER PECUNIARY LOSS) ARISING OF THE USE OR INABILITY TO USE // THE SOFTWARE. // // Motorola assumes no responsibility for the maintenance and support of the // SOFTWARE. // // History // gm 990602: Original code: GMilliorn. // #ifdef GCC #include #else #include #include #endif /* GCC */ #include "errors.h" #include "toks.h" #include "dink.h" #include "par_tb.h" #include "duart.h" #include "gme.h" #include "config.h" extern char process_type; #include extern MMENGINE *SelectMEngine( char *MName ); extern STATUS gme_I2C_acc( unsigned long a, unsigned long *v, short sz, short r_w ); extern STATUS gme_RAM_acc( unsigned long a, unsigned long *v, short sz, short r_w ); extern STATUS gme_NVRAM_acc( unsigned long a, unsigned long *v, short sz, short r_w ); extern void gme_SetDevAddr( unsigned long devaddr ); extern STATUS mem_check( int DoCorrect, int verbose ); extern void init_L2backside_value(); extern void init_L2DM(); extern int SetBaudRate( char *, int ); extern STATUS CommInit( char *device, int baud ); extern STATUS CommLookChar(); extern STATUS CommPutChar(); extern void delay(); extern int init_IO_values(); extern int total_recall(); extern unsigned long strtoul(); extern long strtol(); extern STATUS detect_flash_revision(void); extern STATUS flash_write_to_memory(); extern STATUS flash_sector_erase(int sector); extern STATUS flash_sector_unprotect(int sector); STATUS InitModesEnv(); extern unsigned long KEYBOARD; extern unsigned long HOST; extern MCAPS *mach_info; extern char memdisp_opt; extern char regdisp_opt; extern int TAU_Cal_Value; MMENGINE *me; #ifdef ENV_SUPPORT //--------------------------------------------------------------------------- // Define location and size in NVRAM to use. This might need to change // if we want to share with other programs/OSes/etc. // // The ENV memory is treated as a single string, delimited by newlines for // each variable. // // BASE+0 - BASE+3: "DKNV" -- header, if not found, ENV is unformatted/ // trashed/elsewhere. // BASE+4 - BASE+7: xxxx -- reserved for versioning. // BASE+8 - BASE+11: xxxx -- 16-bit CRC of remaining ENV. // BASE+12 - BASE+n: "VAR=FOO\n" -- a variable. // BASE+o - BASE+p: "TMP=TST\n" -- another variable. // BASE+z "\0" -- end of variables. // // Note: the following definitions are "logical", they need to be offset // to the true address. This is handled in the handler, so logical addrs. // are used througout. #define ENV_HEADER_TAG "DKNV" #define ENV_HEADER_TAG_SIZE (4) #define ENV_STORE_START (12) // Where "real" storage starts #define ENV_MAXSIZE 80 // Max size of any environment var. //--------------------------------------------------------------------------- // Data structure holds the size and starting address of each type of // ENV storage. typedef struct st_EnvParms { unsigned long base; unsigned long size; } ENVPARMS; ENVPARMS EnvParms[] = { { 0x00000000, 0 }, // M_ENVNONE: no ENV { 0x00000000, 4096 }, // M_ENVNVRAM: all available { 0x00000040, 226 }, // M_ENVI2C: 256 bytes, first 40 reserved { 0x000FF000, 1024 }, // M_ENVSRAM: SRAM/SDRAM { 0xFFFFF000, 4096 } // M_ENVFLASH: Flash }; #define ENV_MAX_SIZE (EnvParms[ mach_info->EnvType ].size) #define ENV_STORE_END (ENV_MAX_SIZE - 1) #define ENV_STORE_SIZE (ENV_STORE_END - ENV_STORE_START) //-------------------------------------------------------------------------- // envIO -- do reads or writes to an I/O device for environment // purposes. Uses the generic memory engine (gme_*) to // do the work. //-------------------------------------------------------------------------- STATUS envIO( unsigned long a, unsigned long *v, short sz, short r_w ) { // Add in the base. a += EnvParms[ mach_info->EnvType ].base; // Use the selected handler to do ENV I/O. switch ( mach_info->EnvType ) { case M_ENVI2C: gme_SetDevAddr( 0x57 ); return( gme_I2C_acc( a, v, sz, r_w ) ); case M_ENVNVRAM: return( gme_NVRAM_acc( a, v, sz, r_w ) ); case M_ENVSRAM: return( gme_RAM_acc( a, v, sz, r_w ) ); case M_ENVNONE: break; } return( INVALID ); } //-------------------------------------------------------------------------- // EnvValid -- check environment variable storage status. // //-------------------------------------------------------------------------- STATUS EnvValid() { unsigned long v; short i; char c[4]; for (i = 0; i < 4; i++) { envIO( i, &v, B_ACCESS, GME_R ); c[i] = v; } return( (strncmp( c, ENV_HEADER_TAG, ENV_HEADER_TAG_SIZE ) == 0) ? 1 : 0 ); } //-------------------------------------------------------------------------- // InitEnv -- initialize the environment variable storage status. //-------------------------------------------------------------------------- STATUS InitEnv() { unsigned long a, v; char *s; s = ENV_HEADER_TAG; a = 0; // Write header and preamble. do { v = *s; envIO( a, &v, B_ACCESS, GME_W ); a++; } while (*s++); // Preset remaining memory to zero. v = 0; while (a <= ENV_STORE_END) { envIO( a, &v, B_ACCESS, GME_W ); a++; } return( SUCCESS ); } //-------------------------------------------------------------------------- // PrintEnv -- print environment variables. //-------------------------------------------------------------------------- STATUS PrintEnv( char *name ) { unsigned long a, v; short cfill; char *equal_pt; char c; char tmp[ENV_MAXSIZE + 2]; if (!EnvValid()) return( 0 ); cfill = 0; equal_pt = 0; for (a = ENV_STORE_START; a < ENV_STORE_END; a++) { envIO( a, &v, B_ACCESS, GME_R ); tmp[cfill] = c = (char) v; if (c == '=') // Remember name part equal_pt = &tmp[cfill]; // We've isolated an environment variable. If no name was given as an // argument, show all names. Otherwise, show only matching name(s?). if (c == 0 && cfill == 0) break; if (equal_pt && (c == 0 || c == '\n')) { tmp[++cfill] = '\0'; if (!*name || strncmp( name, tmp, strlen(name) ) == 0) PRINT(" %s", tmp); cfill = 0; equal_pt = 0; } else cfill++; } return( SUCCESS ); } //-------------------------------------------------------------------------- // GetEnvRaw -- get environment variables. Returns actual image, without // any quotes stripped off. Returned value is a pointer to // the matching definition, if any, or 0 if no match. // // The value in "tmp" is the entire string in the form // "name=defn", possibly containing quotes. //-------------------------------------------------------------------------- unsigned long GetEnvRaw( char *name, char **equal_pt, char *tmp ) { short i, sl, el; unsigned long a, v, start_a; if (!EnvValid()) return( 0 ); // Search for a matching definition. start_a = 0; i = 0; *equal_pt = NULL; sl = strlen( name ); for (a = ENV_STORE_START; a <= ENV_STORE_END; a++) { if (start_a == 0) start_a = a; envIO( a, &v, B_ACCESS, GME_R ); tmp[i] = (char) v; if ((!*equal_pt) && (tmp[i] == '=')) // Remember name part *equal_pt = &tmp[i]; tmp[++i] = 0; if (v == 0) // All done... break; else if ((*equal_pt) && (tmp[i-1] == '\n')) { // Got def'n, compare el = (*equal_pt) - tmp; i = sl > el ? sl : el; if (strncmp( name, tmp, i ) == 0) break; i = 0; *equal_pt = 0; start_a = 0; } v = 0; } // If v is non-zero, we found something. return( (v == 0) ? 0 : start_a ); } //-------------------------------------------------------------------------- // GetEnv -- get environment variables. //-------------------------------------------------------------------------- char *GetEnv( char *name, char *def ) { unsigned long v; char *s, *d, *e; if ((v = GetEnvRaw( name, &e, def )) == 0) return( NULL ); s = ++e; // Skip over equals if (*s == '"') // Stomp leading quote s++; for (d = def; *s && *s != '\n'; *d++ = *s++) ; *d = '\0'; if (*(s-1) == '"') // Stomp trailing quote *(d-1) = '\0'; return( def ); } //-------------------------------------------------------------------------- // SetEnv -- set environment variables. //-------------------------------------------------------------------------- STATUS SetEnv( char *name, char *def, short do_delete ) { char tmp[ENV_MAXSIZE]; short cfill; unsigned long a, ar, v; char *equal_pt; char c; if (!EnvValid()) return( 0 ); // PRINT("SetEnv: `%s' to `%s'\n", name, def); // First, search for an existing definition which we will replace. v = GetEnvRaw( name, &equal_pt, tmp ); // Next, delete the definition from memory. If "v" was zero, then // we never found a matching definition, so do nothing. if (v != 0) { cfill = strlen( tmp ); a = v; for (ar = a + cfill; ar <= ENV_STORE_END; a++, ar++) { envIO( ar, &v, B_ACCESS, GME_R ); envIO( a , &v, B_ACCESS, GME_W ); } } // If "do_delete" was set, that's all we need to do. if (do_delete) return( SUCCESS ); // Otherwise, append the new definition to the NVRAM block. for (a = ENV_STORE_START; a < ENV_STORE_END; a++) { envIO( a, &v, B_ACCESS, GME_R ); c = v; if (c == 0) break; } while ((v = *name++) != 0) envIO( a++, &v, B_ACCESS, GME_W ); v = '='; envIO( a++, &v, B_ACCESS, GME_W ); while ((v = *def++) != 0) envIO( a++, &v, B_ACCESS, GME_W ); v = '\n'; envIO( a++, &v, B_ACCESS, GME_W ); v = 0; envIO( a, &v, B_ACCESS, GME_W ); return( SUCCESS ); } //-------------------------------------------------------------------------- // help_env -- environment variable access/control. //-------------------------------------------------------------------------- void help_env() { PRINT( "ENV\n" "===\n" "Mnemonic: env\n" "Syntax: env [-c][-d][-s][var[=value]]\n" "Description: This command displays or sets environment variables stored\n" " in the NVRAM (if available). If no argument is given, the\n" " current settings are displayed. Note: quotes (\") are\n" " usually required if non-alphanumeric characters present.\n" "Flags: -c Clear/Initialize the NVRAM.\n" " -d Delete named variable.\n" " -s Saves environment to permanent storage.\n" ); } //-------------------------------------------------------------------------- // ENVLoad -- load the ENV from permanent storage. This is for Excimer // which uses SRAM mostly and FLASH occasionally. //-------------------------------------------------------------------------- void ENVLoad() { char *s, *d; short stat, i; char c[4]; if (mach_info->EnvType != M_ENVSRAM) return; // Insure local SRAM is not used in any case. d = (char *) EnvParms[ M_ENVSRAM ].base; *d = '0'; // Check the FLASH for the ENV tag. s = (char *) EnvParms[ M_ENVFLASH ].base; for (i = 0; i < 4; i++) c[i] = *s++; stat = (strncmp( c, ENV_HEADER_TAG, ENV_HEADER_TAG_SIZE ) == 0) ? 1 : 0; if (!stat) return; s = (char *) EnvParms[ M_ENVFLASH ].base; for (i = 0; i < EnvParms[ M_ENVSRAM ].size; i++) { // PRINT("%08lx <= 0x%02x (@%08lx)\n", d, *s, s); *d++ = *s++; } PRINT("ENVLoad: ENV loaded from flash.\n"); } //-------------------------------------------------------------------------- // ENVSave -- save the ENV somewhere. This is principally for Excimer, // which uses SRAM and FLASH; other platforms have battery- // backed NVRAM. //-------------------------------------------------------------------------- STATUS ENVSave() { int status, i; int ExcSectorNo; int data, *s, *d; // Check if Excimer/Maximer. if (mach_info->EnvType != M_ENVSRAM) return( INVALID ); ExcSectorNo = 18; detect_flash_revision(); if ((status = flash_sector_unprotect(ExcSectorNo)) == FAILURE) return( INVALID ); PRINT("Sector %d unprotected (ignore error message)\n", ExcSectorNo); if ((status = flash_sector_erase(ExcSectorNo)) == FAILURE) return( INVALID ); // The Excimer flash write routines use 32-bit quantities, so gather // some up. s = (int *) EnvParms[ M_ENVSRAM ].base; d = (int *) EnvParms[ M_ENVFLASH ].base; for (i = 0; i < EnvParms[ M_ENVSRAM ].size; i += 4) { data = *s++; if ((status = flash_write_to_memory( d, data )) == FAILURE) { PRINT("error, %d\n", status); break; } d++; } PRINT("ENV saved to sector %d (0x%lx)\n", ExcSectorNo, d); return( SUCCESS ); } //-------------------------------------------------------------------------- // par_env -- environment variables. //-------------------------------------------------------------------------- STATUS par_env( char *dink_cmd ) { STATUS status; short DoInit, DoSet, DoDelete, DoSave; char *s; TOKEN state; char name[ENV_MAXSIZE]; char def[ENV_MAXSIZE]; // Process arguments as long as they start with a dash. status = SUCCESS; DoInit = 0; DoSet = 0; DoSave = 0; DoDelete = 0; *name = 0; *def = 0; while (tok_is_next_token( DASH_TOK )) { if (tok_is_next_token( BTCL_TOK )) // -c DoInit++; else if (tok_is_next_token( BHWD_TOKEN_D )) // -d DoDelete++; else if (tok_is_next_token( SET_TOK )) // -s DoSave++; } // If there is an argument present, it is a name or a definition. // Look for an '=' to determine whether we print or set. // Note: environment variables are stored as upper-case. if ((status = tok_get_next_token(&state, name)) == SUCCESS) { if (*name == '\n') *name = 0; for (s = name; *s; s++) if (('a' <= *s) && (*s <= 'z')) *s = *s - 0x20; if (tok_is_next_token( UNK_TOK )) { // "=" if ((status = tok_get_next_token(&state, def)) != SUCCESS) return( status ); DoSet = 1; } } // Now do the clear, print or set. if (DoInit) status = InitEnv(); else if (!EnvValid()) { PRINT(" ERROR: Environment invalid, must be initialized.\n"); status = SUCCESS; } else if (DoSet || DoDelete) { status = SetEnv( name, def, DoDelete ); InitModesEnv(); } else if (DoSave) status = ENVSave(); else status = PrintEnv( name ); return( status ); } //-------------------------------------------------------------------------- // L2EnvDef -- matches ENV strings to fields. //-------------------------------------------------------------------------- typedef struct L2EnvDef_st { char field[6]; unsigned long val; } L2ENVDEF; L2ENVDEF l2etab[] = { { "par", 0x40000000 }, { "256K", 0x10000000 }, { "512K", 0x20000000 }, { "1M", 0x30000000 }, { "2M", 0x00000000 }, { "/1.5", 0x04000000 }, // Be sure to put longer strings at top! { "/1", 0x02000000 }, { "/2.5", 0x0A000000 }, { "/2", 0x08000000 }, { "/3.5", 0x06000000 }, { "/3", 0x0C000000 }, { "/4", 0x0E000000 }, { "flow", 0x00000000 }, { "pb", 0x01000000 }, { "pb3", 0x01000000 }, // for '755 { "pb2", 0x00800000 }, { "late", 0x01800000 }, { "do", 0x00400000 }, { "zz", 0x00100000 }, { "wt", 0x00080000 }, { "0.5ns", 0x00000000 }, { "1.0ns", 0x00010000 }, { "1.5ns", 0x00020000 }, { "2.0ns", 0x00030000 }, { "slow", 0x00008000 }, { "diff", 0x00004000 }, { "io", 0x00000400 } }; int l2esize = sizeof(l2etab)/sizeof(L2ENVDEF); //-------------------------------------------------------------------------- // ConvertENVtoL2 -- convert L2 environment variable to L2CR settings. //-------------------------------------------------------------------------- STATUS ConvertENVtoL2( unsigned long *l2cr ) { unsigned long v; STATUS status; unsigned char old_I2C_addr; char tmp[ENV_MAXSIZE]; short i, error; char *c; if (process_type < PPCART) return( FAILURE ); // Look for the "L2CACHE" environment variable. *l2cr = 0; if ((c = GetEnv( "L2CACHE", tmp )) == NULL) { // OK, there's no Sandpoint-based L2 setting. Try looking in the // MPC107-controlled I2C ID EEPROM "L2" field. Only MPC107 boards // have this (well, the MPC824x does, but it has no L2, so who cares?) if (mach_info->BoardID != SP_107) // Only MPC107-boards need try return( INVALID ); me = SelectMEngine( "i2c" ); old_I2C_addr = me->devaddr; // Save default and set to me->devaddr = 0x57; // ID I2C device tmp[0] = 0; status = SUCCESS; for (i = 0; (status == SUCCESS) && (i <= 99); i++) { status = gme_I2C_acc( (unsigned long) i, &v, B_ACCESS, GME_R ); tmp[0] = v & 0xff; } me->devaddr = old_I2C_addr; // Restore default. tmp[ENV_MAXSIZE-1] = 0; if (tmp[0] != 'M' // Check for valid tag. || tmp[1] != 'O' || tmp[2] != 'T') return( INVALID ); c = &tmp[21]; } // First, look for the value 'OFF' or '0'. if ((strncmp( c, "off", 3 ) == 0) || (strcmp( c, "0" ) == 0)) return( INVALID ); // First look for a hex value. if (strncmp( c, "0x", 2) == 0) { v = strtoul( c+2, (char **) NULL, 16 ); *l2cr = v; return( SUCCESS ); } //--------------------------------------------------------------------------- // Look for a symbolic setting. v = 0x00000000; error = 0; while (*c && !error) { while (*c == ' ' || *c == ',') // Skip filler c++; error = 1; for (i = 0; i <= l2esize; i++) if (strncmp( c, l2etab[i].field, strlen(l2etab[i].field) ) == 0) { c += strlen(l2etab[i].field); v |= l2etab[i].val; error = 0; break; } } if (!error) { *l2cr = v; return( SUCCESS ); } return( INVALID ); } //-------------------------------------------------------------------------- // GetIOENV -- Get I/O ENV settings. //-------------------------------------------------------------------------- STATUS GetIOENV() { char tmp[ENV_MAXSIZE]; int status, NewBaud; char *c; // Look for the "IO" environment variable. if ((c = GetEnv( "IO", tmp )) != NULL) { // Parse IO={COM1|PMC|XIO}[:{2400|9600|}] if (strncmp(c, "COM1", 4) == 0) { c += 4; // Since the default is COM1, this is just a placeholder } // Parse PMC -- the on-board UART for PMC cards (only some have it). else if (strncmp(c, "PMC", 3) == 0) { if ((mach_info->BoardID != SP_8240) && (mach_info->BoardID != SP_107) && (mach_info->BoardID != PMC8240)) { PRINT("Only supported on PrPMC cards.\n"); return( INVALID ); } mach_info->IODrivers = M_PMCUART; init_IO_values(); // Re-initialize K/H settings delay( 0x200000 ); // additional delay CommInit( (char *) KEYBOARD, BAUD_9600 ); CommInit( (char *) HOST, BAUD_9600 ); delay( 0x200000 ); // additional delay c += 3; } #ifdef XIO_SUPPORT // XIO -- enable VGA (text mode) and AT keyboard support. else if (strncmp(c, "XIO", 3) == 0) { extern void KeyInit(void); extern int VGAInit( char * ); // Look for and initialize a VGA device. If not found, // fall back to the serial port. c += 3; if (!VGAInit( c )) { PRINT("No VGA device found\n"); return( SUCCESS ); } KeyInit(); mach_info->IODrivers = M_XIO; // Initialize DUART anyway -- might need it. CommInit( (char *) KEYBOARD, BAUD_9600 ); // No baud rate. return( SUCCESS ); } #endif else return( INVALID ); if (*c == ':') c++; // Get baud rate. if ('0' <= *c && *c <= '9') { NewBaud = strtol(c,(char **)NULL, 10); status = SUCCESS; if (NewBaud != 9600) { status = SetBaudRate( (char *) KEYBOARD, NewBaud ); status = SetBaudRate( (char *) HOST, NewBaud ); } return( status ); } } return( INVALID ); } //-------------------------------------------------------------------------- // InitModesEnv -- initialize DINK modes using environment variables. // InitModesEnv can be called more than once. //-------------------------------------------------------------------------- STATUS InitModesEnv() { char *s; char tmp[256]; if (!EnvValid()) return( SUCCESS ); // MDMODE: If set (to anything), use dd/dm in place of md/mm. if ((s = GetEnv( "MDMODE", tmp )) != NULL) memdisp_opt = (char) strtol( s, (char **)NULL, 10 ); // RDMODE: Set the default RD/RM display mode. The string can either // be 'Q' for '-q'/Quiet mode or 'E' for '-e'/Explain mode. if ((s = GetEnv( "RDMODE", tmp )) != NULL) { regdisp_opt = 0x0; if (*s == 'E' || *s == 'e') regdisp_opt = 0x3; // Not verbose and explain fields. else if (*s == 'Q' || *s == 'q') regdisp_opt = 0x1; // Not verbose } return( SUCCESS ); } static char *l2dm_sizes[] = { 0, "256K", "512K", "1M" }; //-------------------------------------------------------------------------- // InitWithEnv -- initialize the system using environment variables, // if possible. //-------------------------------------------------------------------------- STATUS InitWithEnv() { unsigned long val; char *s; char tmp[256]; // If the environment is not defined/initialized, skip settings. if (!EnvValid()) return( SUCCESS ); // If a key is pressed on the keyboard, skip initialization during // start-up. if (CommLookChar((char *) KEYBOARD)) { PRINT("Skipping environment variables setup...\n"); return( SUCCESS ); } // BOOT: If set, jump to indicated location. if ((s = GetEnv( "BOOT", tmp )) != NULL) { val = strtoul( s, (char **)NULL, 16 ); PRINT("Booting from 0x%08lx ...\n\n", val); total_recall( val ); } // L2DIRECT: If set, set L2DM register (before L2CR is set). if ((s = GetEnv( "L2DIRECT", tmp )) != NULL) { val = strtoul( s, (char **)NULL, 16 ); if (val) { init_L2DM( val ); PRINT("ENV: L2DM set to %s @ 0x%08lx\n", l2dm_sizes[val&0x03], (val & 0xFFFF0000) ); } } // L2CACHE: If set, use to enable L2CACHE. if (ConvertENVtoL2( &val ) == SUCCESS) { PRINT("ENV: Set L2 to %08lx\n", val); init_L2backside_value( val ); } // IO: If set, we may change from default serial port usage. // If we changed the I/O system at all we have to re-initialize. GetIOENV(); // MEMOPT: If set (to anything), call the memory optimizer. if ((GetEnv( "MEMOPT", tmp )) != NULL) if (mem_check( 1, 0 ) == SUCCESS) PRINT("Memory settings optimized...\n"); // ALIAS: // ALIAS=whatever if ((s = GetEnv( "ALIAS", tmp )) != NULL) { strcat( s, "\n" ); strcat( s, "\n" ); memset( alias_string, 256, 0 ); strcpy( alias_string, s ); } // TAUCAL: If set, it is a hex calibration value for the TAU. #ifdef TAU_SUPPORT if ((s = GetEnv( "TAUCAL", tmp )) != NULL) TAU_Cal_Value = strtol( s, (char **)NULL, 16 ); #endif TAU_SUPPORT // Now initialize modes, which are changeable after reset as well. InitModesEnv(); return( SUCCESS ); } #endif ENV_SUPPORT