This is Colin O'Flynn's mega patch for updating safemode support:

* add support for parts with just 'fuse' memory

    * if any fuse memories are altered, reflect those changes in the
      post-programming safemode check so that safemode no longer
      complains about fuses which were intentionally altered; this
      eliminates the need to completely disable safemode using -u in
      order to program fuses.

    * provide -s option which will not ask to restore fuses, it will
      just do it

Submitted by: Colin O'Flynn <coflynn@newae.com>


git-svn-id: svn://svn.savannah.nongnu.org/avrdude/trunk@519 81a1dc3b-b13d-400b-aceb-764788c761c2
This commit is contained in:
Brian S. Dean 2005-09-21 00:20:32 +00:00
parent 56b04c2511
commit e2a99a00a4
5 changed files with 204 additions and 97 deletions

View File

@ -33,6 +33,7 @@
#include "lists.h" #include "lists.h"
#include "pindefs.h" #include "pindefs.h"
#include "ppi.h" #include "ppi.h"
#include "safemode.h"
#define DEBUG 0 #define DEBUG 0
@ -479,6 +480,30 @@ int avr_write_byte_default(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
int avr_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, int avr_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
unsigned long addr, unsigned char data) unsigned long addr, unsigned char data)
{ {
unsigned char safemode_lfuse;
unsigned char safemode_hfuse;
unsigned char safemode_efuse;
unsigned char safemode_fuse;
/* If we write the fuses, then we need to tell safemode that they *should* change */
safemode_memfuses(0, &safemode_lfuse, &safemode_hfuse, &safemode_efuse, &safemode_fuse);
if (strcmp(mem->desc, "fuse")==0) {
safemode_fuse = data;
}
if (strcmp(mem->desc, "lfuse")==0) {
safemode_lfuse = data;
}
if (strcmp(mem->desc, "hfuse")==0) {
safemode_hfuse = data;
}
if (strcmp(mem->desc, "efuse")==0) {
safemode_efuse = data;
}
safemode_memfuses(1, &safemode_lfuse, &safemode_hfuse, &safemode_efuse, &safemode_fuse);
int rc; int rc;
if (pgm->write_byte) { if (pgm->write_byte) {
@ -592,6 +617,7 @@ int avr_write(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size,
pgm->err_led(pgm, ON); pgm->err_led(pgm, ON);
} }
} }
return i; return i;
} }

View File

@ -114,9 +114,9 @@ void usage(void)
" is performed in the order specified.\n" " is performed in the order specified.\n"
" -n Do not write anything to the device.\n" " -n Do not write anything to the device.\n"
" -V Do not verify.\n" " -V Do not verify.\n"
" -u Disable safemode, you need this option if you\n" " -u Disable safemode, default when running from a script.\n"
" want to change fuse bits. Otherwise they will be\n" " -s Silent safemode operation, will not ask you if\n"
" recovered if they change\n" " fuses should be changed back.\n"
" -t Enter terminal mode.\n" " -t Enter terminal mode.\n"
" -E <exitspec>[,<exitspec>] List programmer exit specifications.\n" " -E <exitspec>[,<exitspec>] List programmer exit specifications.\n"
" -y Count # erase cycles in EEPROM.\n" " -y Count # erase cycles in EEPROM.\n"
@ -729,9 +729,13 @@ int main(int argc, char * argv [])
int baudrate; /* override default programmer baud rate */ int baudrate; /* override default programmer baud rate */
double bitclock; /* Specify programmer bit clock (JTAG ICE) */ double bitclock; /* Specify programmer bit clock (JTAG ICE) */
int safemode; /* Enable safemode, 1=safemode on, 0=normal */ int safemode; /* Enable safemode, 1=safemode on, 0=normal */
int silentsafe; /* Don't ask about fuses, 1=silent, 0=normal */
unsigned char safemode_lfuse = 0xff; unsigned char safemode_lfuse = 0xff;
unsigned char safemode_hfuse = 0xff; unsigned char safemode_hfuse = 0xff;
unsigned char safemode_efuse = 0xff; unsigned char safemode_efuse = 0xff;
unsigned char safemode_fuse = 0xff;
char * safemode_response;
int fuses_specified = 0; int fuses_specified = 0;
int fuses_updated = 0; int fuses_updated = 0;
#if !defined(WIN32NATIVE) #if !defined(WIN32NATIVE)
@ -781,7 +785,12 @@ int main(int argc, char * argv [])
set_cycles = -1; set_cycles = -1;
baudrate = 0; baudrate = 0;
bitclock = 0.0; bitclock = 0.0;
safemode = 1; /* Safemode enabled by default */ safemode = 1; /* Safemode on by default */
silentsafe = 0; /* Ask by default */
if (isatty(STDIN_FILENO) == 0)
safemode = 0; /* Turn off safemode if this isn't a terminal */
#if defined(WIN32NATIVE) #if defined(WIN32NATIVE)
@ -826,7 +835,7 @@ int main(int argc, char * argv [])
/* /*
* process command line arguments * process command line arguments
*/ */
while ((ch = getopt(argc,argv,"?b:c:C:DeE:Fnp:P:qtU:uvVyY:")) != -1) { while ((ch = getopt(argc,argv,"?b:c:C:DeE:Fnp:P:qtU:suvVyY:")) != -1) {
switch (ch) { switch (ch) {
case 'b': /* override default programmer baud rate */ case 'b': /* override default programmer baud rate */
@ -888,6 +897,11 @@ int main(int argc, char * argv [])
quell_progress++ ; quell_progress++ ;
break; break;
case 's' : /* Silent safemode */
silentsafe = 1;
safemode = 1;
break;
case 't': /* enter terminal mode */ case 't': /* enter terminal mode */
terminal = 1; terminal = 1;
break; break;
@ -1271,17 +1285,11 @@ int main(int argc, char * argv [])
} }
if (safemode == 1) { if (safemode == 1) {
AVRMEM * m;
/* If safemode is enabled, go ahead and read the current low, high, /* If safemode is enabled, go ahead and read the current low, high,
and extended fuse bytes as needed */ and extended fuse bytes as needed */
if (quell_progress < 2) {
fprintf(stderr, "\n");
}
if (safemode_readfuses(&safemode_lfuse, &safemode_hfuse, if (safemode_readfuses(&safemode_lfuse, &safemode_hfuse,
&safemode_efuse, pgm, p, verbose) != 0) { &safemode_efuse, &safemode_fuse, pgm, p, verbose) != 0) {
fprintf(stderr, "%s: safemode: To protect your AVR the programming " fprintf(stderr, "%s: safemode: To protect your AVR the programming "
"will be aborted\n", "will be aborted\n",
progname); progname);
@ -1289,36 +1297,8 @@ int main(int argc, char * argv [])
goto main_exit; goto main_exit;
} }
if (quell_progress < 2) {
fprintf(stderr, "\n");
}
//Save the fuses as default //Save the fuses as default
safemode_memfuses(1, &safemode_lfuse, &safemode_hfuse, &safemode_efuse); safemode_memfuses(1, &safemode_lfuse, &safemode_hfuse, &safemode_efuse, &safemode_fuse);
/* Check if user is attempting to write fuse bytes */
for (ln=lfirst(updates); ln; ln=lnext(ln)) {
upd = ldata(ln);
m = avr_locate_mem(p, upd->memtype);
if (m == NULL)
continue;
if (((strcasecmp(m->desc, "lfuse") == 0) ||
(strcasecmp(m->desc, "hfuse") == 0) ||
(strcasecmp(m->desc, "efuse") == 0)) && (upd->op == DEVICE_WRITE)) {
fuses_specified = 1;
fprintf(stderr,
"%s: NOTE: FUSE memory has been specified, and safemode is ON\n"
"%s: This will not allow you to change the fuse bits.\n"
"%s: To disable this feature, specify the -u option.\n",
progname, progname, progname);
}
}
if (quell_progress < 2) {
fprintf(stderr, "\n");
}
} }
@ -1403,13 +1383,6 @@ int main(int argc, char * argv [])
if (terminal) { if (terminal) {
/* Warn user if safemode is on */
if (safemode > 0) {
fprintf(stderr, "%s: safemode is enabled, you will NOT be "
"able to change the fuse bits. Use -u option to disable\n",
progname);
}
/* /*
* terminal mode * terminal mode
*/ */
@ -1433,22 +1406,24 @@ int main(int argc, char * argv [])
* high, and extended fuse bytes as needed */ * high, and extended fuse bytes as needed */
unsigned char safemodeafter_lfuse = 0xff; unsigned char safemodeafter_lfuse = 0xff;
unsigned char safemodeafter_hfuse = 0xff; unsigned char safemodeafter_hfuse = 0xff;
unsigned char safemodeafter_efuse = 0xff; unsigned char safemodeafter_efuse = 0xff;
unsigned char safemodeafter_fuse = 0xff;
unsigned char failures = 0; unsigned char failures = 0;
char yes[1] = {'y'};
if (quell_progress < 2) { if (quell_progress < 2) {
fprintf(stderr, "\n"); fprintf(stderr, "\n");
} }
//Restore the default fuse values //Restore the default fuse values
safemode_memfuses(0, &safemode_lfuse, &safemode_hfuse, &safemode_efuse); safemode_memfuses(0, &safemode_lfuse, &safemode_hfuse, &safemode_efuse, &safemode_fuse);
/* Try reading back fuses, make sure they are reliable to read back */ /* Try reading back fuses, make sure they are reliable to read back */
if (safemode_readfuses(&safemodeafter_lfuse, &safemodeafter_hfuse, if (safemode_readfuses(&safemodeafter_lfuse, &safemodeafter_hfuse,
&safemodeafter_efuse, pgm, p, verbose) != 0) { &safemodeafter_efuse, &safemode_fuse, pgm, p, verbose) != 0) {
/* Uh-oh.. try once more to read back fuses */ /* Uh-oh.. try once more to read back fuses */
if (safemode_readfuses(&safemodeafter_lfuse, &safemodeafter_hfuse, if (safemode_readfuses(&safemodeafter_lfuse, &safemodeafter_hfuse,
&safemodeafter_efuse, pgm, p, verbose) != 0) { &safemodeafter_efuse, &safemodeafter_fuse, pgm, p, verbose) != 0) {
fprintf(stderr, fprintf(stderr,
"%s: safemode: Sorry, reading back fuses was unreliable. " "%s: safemode: Sorry, reading back fuses was unreliable. "
"I have given up and exited programming mode\n", "I have given up and exited programming mode\n",
@ -1457,56 +1432,111 @@ int main(int argc, char * argv [])
goto main_exit; goto main_exit;
} }
} }
/* Now check what fuses are against what they should be */
if (safemodeafter_fuse != safemode_fuse) {
fuses_updated = 1;
fprintf(stderr, "%s: safemode: fuse changed! Was %x, and is now %x\n",
progname, safemode_fuse, safemodeafter_fuse);
/* Ask user - should we change them */
if (silentsafe == 0)
safemode_response = terminal_get_input("Would you like this fuse to be changed back? [y/n] ");
else
safemode_response = yes;
if (tolower(safemode_response[0]) == 'y') {
/* Enough chit-chat, time to program some fuses and check them */
if (safemode_writefuse (safemode_fuse, "fuse", pgm, p,
10, verbose) == 0) {
fprintf(stderr, "%s: safemode: and is now rescued\n", progname);
}
else {
fprintf(stderr, "%s: and COULD NOT be changed\n", progname);
failures++;
}
}
}
/* Now check what fuses are against what they should be */ /* Now check what fuses are against what they should be */
if (safemodeafter_lfuse != safemode_lfuse) { if (safemodeafter_lfuse != safemode_lfuse) {
fuses_updated = 1; fuses_updated = 1;
fprintf(stderr, "%s: safemode: lfuse changed! Read as %x, was %x\n", fprintf(stderr, "%s: safemode: lfuse changed! Was %x, and is now %x\n",
progname, safemodeafter_lfuse, safemode_lfuse); progname, safemode_lfuse, safemodeafter_lfuse);
/* Enough chit-chat, time to program some fuses and check them */
if (safemode_writefuse (safemode_lfuse, "lfuse", pgm, p, /* Ask user - should we change them */
10, verbose) == 0) {
fprintf(stderr, "%s: safemode: and is now rescued\n", progname); if (silentsafe == 0)
} safemode_response = terminal_get_input("Would you like this fuse to be changed back? [y/n] ");
else { else
fprintf(stderr, "%s: and COULD NOT be changed\n", progname); safemode_response = yes;
failures++;
if (tolower(safemode_response[0]) == 'y') {
/* Enough chit-chat, time to program some fuses and check them */
if (safemode_writefuse (safemode_lfuse, "lfuse", pgm, p,
10, verbose) == 0) {
fprintf(stderr, "%s: safemode: and is now rescued\n", progname);
}
else {
fprintf(stderr, "%s: and COULD NOT be changed\n", progname);
failures++;
}
} }
} }
/* Now check what fuses are against what they should be */ /* Now check what fuses are against what they should be */
if (safemodeafter_hfuse != safemode_hfuse) { if (safemodeafter_hfuse != safemode_hfuse) {
fuses_updated = 1; fuses_updated = 1;
fprintf(stderr, "%s: safemode: hfuse changed! Read as %x, was %x\n", fprintf(stderr, "%s: safemode: hfuse changed! Was %x, and is now %x\n",
progname, safemodeafter_hfuse, safemode_hfuse); progname, safemode_hfuse, safemodeafter_hfuse);
/* Ask user - should we change them */
if (silentsafe == 0)
safemode_response = terminal_get_input("Would you like this fuse to be changed back? [y/n] ");
else
safemode_response = yes;
if (tolower(safemode_response[0]) == 'y') {
/* Enough chit-chat, time to program some fuses and check them */ /* Enough chit-chat, time to program some fuses and check them */
if (safemode_writefuse(safemode_hfuse, "hfuse", pgm, p, if (safemode_writefuse(safemode_hfuse, "hfuse", pgm, p,
10, verbose) == 0) { 10, verbose) == 0) {
fprintf(stderr, "%s: safemode: and is now rescued\n", progname); fprintf(stderr, "%s: safemode: and is now rescued\n", progname);
} }
else { else {
fprintf(stderr, "%s: and COULD NOT be changed\n", progname); fprintf(stderr, "%s: and COULD NOT be changed\n", progname);
failures++; failures++;
}
} }
} }
/* Now check what fuses are against what they should be */ /* Now check what fuses are against what they should be */
if (safemodeafter_efuse != safemode_efuse) { if (safemodeafter_efuse != safemode_efuse) {
fuses_updated = 1; fuses_updated = 1;
fprintf(stderr, "%s: safemode: efuse changed! Read as %x, was %x\n", fprintf(stderr, "%s: safemode: efuse changed! Was %x, and is now %x\n",
progname, safemodeafter_efuse, safemode_efuse); progname, safemode_efuse, safemodeafter_efuse);
/* Enough chit-chat, time to program some fuses and check them */ /* Ask user - should we change them */
if (safemode_writefuse (safemode_efuse, "efuse", pgm, p, if (silentsafe == 0)
10, verbose) == 0) { safemode_response = terminal_get_input("Would you like this fuse to be changed back? [y/n] ");
fprintf(stderr, "%s: safemode: and is now rescued\n", progname); else
} safemode_response = yes;
else { if (tolower(safemode_response[0]) == 'y') {
fprintf(stderr, "%s: and COULD NOT be changed\n", progname);
failures++; /* Enough chit-chat, time to program some fuses and check them */
} if (safemode_writefuse (safemode_efuse, "efuse", pgm, p,
10, verbose) == 0) {
fprintf(stderr, "%s: safemode: and is now rescued\n", progname);
}
else {
fprintf(stderr, "%s: and COULD NOT be changed\n", progname);
failures++;
}
}
} }
if (quell_progress < 2) { if (quell_progress < 2) {

View File

@ -30,9 +30,13 @@
/* This value from ac_cfg.h */ /* This value from ac_cfg.h */
char * progname = PACKAGE_NAME; char * progname = PACKAGE_NAME;
/* Writes the specified fuse in fusename (can be "lfuse", "hfuse", or "efuse") and verifies it. Will try up to tries /*
amount of times before giving up */ * Writes the specified fuse in fusename (can be "lfuse", "hfuse", or
int safemode_writefuse (unsigned char fuse, char * fusename, PROGRAMMER * pgm, AVRPART * p, int tries, int verbose) * "efuse") and verifies it. Will try up to tries amount of times
* before giving up
*/
int safemode_writefuse (unsigned char fuse, char * fusename, PROGRAMMER * pgm,
AVRPART * p, int tries, int verbose)
{ {
AVRMEM * m; AVRMEM * m;
unsigned char fuseread; unsigned char fuseread;
@ -50,8 +54,9 @@ int safemode_writefuse (unsigned char fuse, char * fusename, PROGRAMMER * pgm, A
/* Report information to user if needed */ /* Report information to user if needed */
if (verbose > 0) { if (verbose > 0) {
fprintf(stderr, "%s: safemode: Wrote %s to %x, read as %x. %d attempts left\n", fprintf(stderr,
progname, fusename, fuse, fuseread, tries-1); "%s: safemode: Wrote %s to %x, read as %x. %d attempts left\n",
progname, fusename, fuse, fuseread, tries-1);
} }
/* If fuse wrote OK, no need to keep going */ /* If fuse wrote OK, no need to keep going */
@ -65,8 +70,13 @@ int safemode_writefuse (unsigned char fuse, char * fusename, PROGRAMMER * pgm, A
return returnvalue; return returnvalue;
} }
/* Reads the fuses three times, checking that all readings are the same. This will ensure that the before values aren't in error! */ /*
int safemode_readfuses (unsigned char * lfuse, unsigned char * hfuse, unsigned char * efuse, PROGRAMMER * pgm, AVRPART * p, int verbose) * Reads the fuses three times, checking that all readings are the
* same. This will ensure that the before values aren't in error!
*/
int safemode_readfuses (unsigned char * lfuse, unsigned char * hfuse,
unsigned char * efuse, unsigned char * fuse,
PROGRAMMER * pgm, AVRPART * p, int verbose)
{ {
unsigned char value; unsigned char value;
@ -74,11 +84,41 @@ int safemode_readfuses (unsigned char * lfuse, unsigned char * hfuse, unsigned c
unsigned char safemode_lfuse; unsigned char safemode_lfuse;
unsigned char safemode_hfuse; unsigned char safemode_hfuse;
unsigned char safemode_efuse; unsigned char safemode_efuse;
unsigned char safemode_fuse;
AVRMEM * m; AVRMEM * m;
safemode_lfuse = *lfuse; safemode_lfuse = *lfuse;
safemode_hfuse = *hfuse; safemode_hfuse = *hfuse;
safemode_efuse = *efuse; safemode_efuse = *efuse;
safemode_fuse = *fuse;
/* Read fuse three times */
fusegood = 2; /* If AVR device doesn't support this fuse, don't want
to generate a verify error */
m = avr_locate_mem(p, "fuse");
if (m != NULL) {
fusegood = 0; /* By default fuse is a failure */
avr_read_byte(pgm, p, m, 0, &safemode_fuse);
avr_read_byte(pgm, p, m, 0, &value);
if (value == safemode_fuse) {
avr_read_byte(pgm, p, m, 0, &value);
if (value == safemode_fuse){
fusegood = 1; /* Fuse read OK three times */
}
}
}
if (fusegood == 0) {
fprintf(stderr,
"%s: safemode: Verify error - unable to read fuse properly. "
"Programmer may not be reliable.\n", progname);
return -1;
}
else if ((fusegood == 1) && (verbose > 0)) {
printf("%s: safemode: fuse reads as %X\n", progname, safemode_fuse);
}
/* Read lfuse three times */ /* Read lfuse three times */
fusegood = 2; /* If AVR device doesn't support this fuse, don't want fusegood = 2; /* If AVR device doesn't support this fuse, don't want
@ -161,20 +201,28 @@ int safemode_readfuses (unsigned char * lfuse, unsigned char * hfuse, unsigned c
*lfuse = safemode_lfuse; *lfuse = safemode_lfuse;
*hfuse = safemode_hfuse; *hfuse = safemode_hfuse;
*efuse = safemode_efuse; *efuse = safemode_efuse;
*fuse = safemode_fuse;
return 0; return 0;
} }
/* This routine will store the current values pointed to by lfuse, hfuse, and efuse into an internal buffer in this routine /*
when save is set to 1. When save is 0 (or not 1 really) it will copy the values from the internal buffer into the locations * This routine will store the current values pointed to by lfuse,
pointed to be lfuse, hfuse, and efuse. This allows you to change the fuse bits if needed from another routine (ie: have it so * hfuse, and efuse into an internal buffer in this routine when save
if user requests fuse bits are changed, the requested value is now verified */ * is set to 1. When save is 0 (or not 1 really) it will copy the
int safemode_memfuses (int save, unsigned char * lfuse, unsigned char * hfuse, unsigned char * efuse) * values from the internal buffer into the locations pointed to be
* lfuse, hfuse, and efuse. This allows you to change the fuse bits if
* needed from another routine (ie: have it so if user requests fuse
* bits are changed, the requested value is now verified
*/
int safemode_memfuses (int save, unsigned char * lfuse, unsigned char * hfuse,
unsigned char * efuse, unsigned char * fuse)
{ {
static unsigned char safemode_lfuse = 0xff; static unsigned char safemode_lfuse = 0xff;
static unsigned char safemode_hfuse = 0xff; static unsigned char safemode_hfuse = 0xff;
static unsigned char safemode_efuse = 0xff; static unsigned char safemode_efuse = 0xff;
static unsigned char safemode_fuse = 0xff;
switch (save) { switch (save) {
@ -183,6 +231,7 @@ int safemode_memfuses (int save, unsigned char * lfuse, unsigned char * hfuse, u
safemode_lfuse = *lfuse; safemode_lfuse = *lfuse;
safemode_hfuse = *hfuse; safemode_hfuse = *hfuse;
safemode_efuse = *efuse; safemode_efuse = *efuse;
safemode_fuse = *fuse;
break; break;
/* Read back the fuses */ /* Read back the fuses */
@ -190,6 +239,7 @@ int safemode_memfuses (int save, unsigned char * lfuse, unsigned char * hfuse, u
*lfuse = safemode_lfuse; *lfuse = safemode_lfuse;
*hfuse = safemode_hfuse; *hfuse = safemode_hfuse;
*efuse = safemode_efuse; *efuse = safemode_efuse;
*fuse = safemode_fuse;
break; break;
} }

View File

@ -28,12 +28,12 @@ amount of times before giving up */
int safemode_writefuse (unsigned char fuse, char * fusename, PROGRAMMER * pgm, AVRPART * p, int tries, int verbose); int safemode_writefuse (unsigned char fuse, char * fusename, PROGRAMMER * pgm, AVRPART * p, int tries, int verbose);
/* Reads the fuses three times, checking that all readings are the same. This will ensure that the before values aren't in error! */ /* Reads the fuses three times, checking that all readings are the same. This will ensure that the before values aren't in error! */
int safemode_readfuses (unsigned char * lfuse, unsigned char * hfuse, unsigned char * efuse, PROGRAMMER * pgm, AVRPART * p, int verbose); int safemode_readfuses (unsigned char * lfuse, unsigned char * hfuse, unsigned char * efuse, unsigned char * fuse, PROGRAMMER * pgm, AVRPART * p, int verbose);
/* This routine will store the current values pointed to by lfuse, hfuse, and efuse into an internal buffer in this routine /* This routine will store the current values pointed to by lfuse, hfuse, and efuse into an internal buffer in this routine
when save is set to 1. When save is 0 (or not 1 really) it will copy the values from the internal buffer into the locations when save is set to 1. When save is 0 (or not 1 really) it will copy the values from the internal buffer into the locations
pointed to be lfuse, hfuse, and efuse. This allows you to change the fuse bits if needed from another routine (ie: have it so pointed to be lfuse, hfuse, and efuse. This allows you to change the fuse bits if needed from another routine (ie: have it so
if user requests fuse bits are changed, the requested value is now verified */ if user requests fuse bits are changed, the requested value is now verified */
int safemode_memfuses (int save, unsigned char * lfuse, unsigned char * hfuse, unsigned char * efuse); int safemode_memfuses (int save, unsigned char * lfuse, unsigned char * hfuse, unsigned char * efuse, unsigned char * fuse);
#endif //__safemode_h #endif //__safemode_h

View File

@ -26,5 +26,6 @@
#include "pgm.h" #include "pgm.h"
int terminal_mode(PROGRAMMER * pgm, struct avrpart * p); int terminal_mode(PROGRAMMER * pgm, struct avrpart * p);
char * terminal_get_input(const char *prompt);
#endif #endif