1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Plugin for reprogramming only the second image in Flash ROM.
11 * !!! DON'T MESS WITH THIS CODE UNLESS YOU'RE ABSOLUTELY SURE WHAT YOU DO !!!
13 * Copyright (C) 2003 Jörg Hohensohn aka [IDC]Dragon
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
23 ****************************************************************************/
26 /* define DUMMY if you only want to "play" with the UI, does no harm */
29 #define LATEST_BOOTLOADER_VERSION 3 /* update this with the bootloader */
32 #define UINT8 unsigned char
36 #define UINT16 unsigned short
40 #define UINT32 unsigned long
43 /* hard-coded values */
44 static volatile UINT8
* FB
= (UINT8
*)0x02000000; /* Flash base address */
45 #define SECTORSIZE 4096 /* size of one flash sector */
47 #define ROCKBOX_DEST 0x09000000
48 #define ROCKBOX_EXEC 0x09000200
49 #define BOOT_VERS_ADR 0xFA /* position of bootloader version value in Flash */
50 #define FW_VERS_ADR 0xFE /* position of firmware version value in Flash */
51 #define UCL_HEADER 26 /* size of the header generated by uclpack */
53 #if CONFIG_KEYPAD == ONDIO_PAD /* limited keypad */
54 #define KEY1 BUTTON_LEFT
55 #define KEY2 BUTTON_UP
56 #define KEYNAME1 "Left"
58 #else /* recorder keypad */
59 #define KEY1 BUTTON_F1
60 #define KEY2 BUTTON_F2
67 UINT32 destination
; /* address to copy it to */
68 UINT32 size
; /* how many bytes of payload (to the next header) */
69 UINT32 execute
; /* entry point */
70 UINT32 flags
; /* uncompressed or compressed */
71 /* end of header, now comes the payload */
74 /* result of the CheckFirmwareFile() function */
78 eFileNotFound
, /* errors from here on */
96 static UINT8
* sector
; /* better not place this on the stack... */
98 /***************** Flash Functions *****************/
101 /* read the manufacturer and device ID */
102 static bool ReadID(volatile UINT8
* pBase
, UINT8
* pManufacturerID
,
105 UINT8 not_manu
, not_id
; /* read values before switching to ID mode */
106 UINT8 manu
, id
; /* read values when in ID mode */
108 pBase
= (UINT8
*)((UINT32
)pBase
& 0xFFF80000); /* round down to 512k align,
111 not_manu
= pBase
[0]; /* read the normal content */
112 not_id
= pBase
[1]; /* should be 'A' (0x41) and 'R' (0x52) from the
115 pBase
[0x5555] = 0xAA; /* enter command mode */
116 pBase
[0x2AAA] = 0x55;
117 pBase
[0x5555] = 0x90; /* ID command */
118 rb
->sleep(HZ
/50); /* Atmel wants 20ms pause here */
123 pBase
[0] = 0xF0; /* reset flash (back to normal read mode) */
124 rb
->sleep(HZ
/50); /* Atmel wants 20ms pause here */
126 /* I assume success if the obtained values are different from
127 the normal flash content. This is not perfectly bulletproof, they
128 could theoretically be the same by chance, causing us to fail. */
129 if (not_manu
!= manu
|| not_id
!= id
) /* a value has changed */
131 *pManufacturerID
= manu
; /* return the results */
133 return true; /* success */
135 return false; /* fail */
138 /* erase the sector which contains the given address */
139 static bool EraseSector(volatile UINT8
* pAddr
)
142 (void)pAddr
; /* prevents warning */
145 volatile UINT8
* pBase
=
146 (UINT8
*)((UINT32
)pAddr
& 0xFFF80000); /* round down to 512k align */
147 unsigned timeout
= 43000; /* the timeout loop should be no less than
150 pBase
[0x5555] = 0xAA; /* enter command mode */
151 pBase
[0x2AAA] = 0x55;
152 pBase
[0x5555] = 0x80; /* erase command */
153 pBase
[0x5555] = 0xAA; /* enter command mode */
154 pBase
[0x2AAA] = 0x55;
155 *pAddr
= 0x30; /* erase the sector */
157 /* I counted 7 instructions for this loop -> min. 0.58 us per round
158 Plus memory waitstates it will be much more, gives margin */
159 while (*pAddr
!= 0xFF && --timeout
); /* poll for erased */
161 return (timeout
!= 0);
165 /* address must be in an erased location */
166 static inline bool ProgramByte(volatile UINT8
* pAddr
, UINT8 data
)
169 (void)pAddr
; /* prevents warnings */
173 unsigned timeout
= 35; /* the timeout loop should be no less than 20us */
175 if (~*pAddr
& data
) /* just a safety feature, not really necessary */
176 return false; /* can't set any bit from 0 to 1 */
178 FB
[0x5555] = 0xAA; /* enter command mode */
180 FB
[0x5555] = 0xA0; /* byte program command */
184 /* I counted 7 instructions for this loop -> min. 0.58 us per round
185 Plus memory waitstates it will be much more, gives margin */
186 while (*pAddr
!= data
&& --timeout
); /* poll for programmed */
188 return (timeout
!= 0);
192 /* this returns true if supported and fills the info struct */
193 static bool GetFlashInfo(tFlashInfo
* pInfo
)
195 rb
->memset(pInfo
, 0, sizeof(tFlashInfo
));
197 if (!ReadID(FB
, &pInfo
->manufacturer
, &pInfo
->id
))
200 if (pInfo
->manufacturer
== 0xBF) /* SST */
202 if (pInfo
->id
== 0xD6)
204 pInfo
->size
= 256* 1024; /* 256k */
205 rb
->strcpy(pInfo
->name
, "SST39VF020");
208 else if (pInfo
->id
== 0xD7)
210 pInfo
->size
= 512* 1024; /* 512k */
211 rb
->strcpy(pInfo
->name
, "SST39VF040");
221 /*********** Tool Functions ************/
223 /* read a 32 bit value from memory, big endian */
224 static UINT32
Read32(UINT8
* pByte
)
228 value
= (UINT32
)pByte
[0] << 24;
229 value
|= (UINT32
)pByte
[1] << 16;
230 value
|= (UINT32
)pByte
[2] << 8;
231 value
|= (UINT32
)pByte
[3];
236 /* get the start address of the second image */
237 static tImageHeader
* GetSecondImage(void)
239 tImageHeader
* pImage1
;
240 UINT32 pos
= 0; /* default: not found */
241 UINT32
* pFlash
= (UINT32
*)FB
;
243 /* determine the first image position */
244 pos
= pFlash
[2] + pFlash
[3]; /* position + size of the bootloader
246 pos
= (pos
+ 3) & ~3; /* be sure it's 32 bit aligned */
247 pImage1
= (tImageHeader
*)pos
;
249 if (pImage1
->destination
!= ROCKBOX_DEST
||
250 pImage1
->execute
!= ROCKBOX_EXEC
)
251 return 0; /* seems to be no Archos/Rockbox image in here */
253 if (pImage1
->size
!= 0)
255 /* success, we have a second image */
256 pos
= (UINT32
)pImage1
+ sizeof(tImageHeader
) + pImage1
->size
;
257 if (((pos
+ SECTORSIZE
-1) & ~(SECTORSIZE
-1)) != pos
)
258 { /* not sector-aligned */
259 pos
= 0; /* sanity check failed */
263 return (tImageHeader
*)pos
;
266 /* return bootloader version */
267 static inline unsigned BootloaderVersion(void)
269 return FB
[BOOT_VERS_ADR
];
272 /*********** Image File Functions ************/
274 /* so far, only compressed images in UCL NRV algorithm 2e supported */
275 tCheckResult
CheckImageFile(char* filename
, int space
,
276 tImageHeader
* pHeader
, UINT8
* pos
)
280 int filesize
; /* size info */
282 int fileread
= 0; /* total size as read from the file */
283 int read
; /* how many for this sector */
285 /* magic file header for compressed files */
286 static const UINT8 magic
[8] = { 0x00,0xe9,0x55,0x43,0x4c,0xff,0x01,0x1a };
287 UINT8 ucl_header
[UCL_HEADER
];
289 fd
= rb
->open(filename
, O_RDONLY
);
291 return eFileNotFound
;
293 filesize
= rb
->filesize(fd
);
294 if (filesize
- (int)sizeof(ucl_header
) - 8 > space
)
299 else if (filesize
< 10000) /* give it some reasonable lower limit */
305 /* do some sanity checks */
307 read
= rb
->read(fd
, ucl_header
, sizeof(ucl_header
));
309 if (read
!= sizeof(ucl_header
))
315 /* compare the magic header */
318 if (ucl_header
[i
] != magic
[i
])
325 pHeader
->size
= Read32(ucl_header
+ 22); /* compressed size */
326 if (pHeader
->size
!= filesize
- sizeof(ucl_header
) - 8)
332 /* fill in the hardcoded defaults of the header */
333 pHeader
->destination
= ROCKBOX_DEST
;
334 pHeader
->execute
= ROCKBOX_EXEC
;
336 if (Read32(ucl_header
+ 18) > pHeader
->size
) /* compare with uncompressed
338 { /* compressed, normal case */
339 pHeader
->flags
= 0x00000001; /* flags for UCL compressed */
341 /* check for supported algorithm */
342 if (ucl_header
[12] != 0x2E)
345 return eWrongAlgorithm
;
349 { /* uncompressed, either to be copied or run directly in flash */
350 UINT32 reset_vector
; /* image has to start with reset vector */
352 pHeader
->flags
= 0x00000000; /* uncompressed */
354 read
= rb
->read(fd
, &reset_vector
, sizeof(reset_vector
));
356 if (read
!= sizeof(reset_vector
))
361 if (reset_vector
>= (UINT32
)FB
362 && reset_vector
< (UINT32
)FB
+512*1024) /* ROM address? */
364 /* assume in-place, executing directly in flash */
365 pHeader
->destination
= (UINT32
)(pos
+ sizeof(tImageHeader
));
367 /* for new RomBox, this isn't the reset vector,
368 but the link address, for us to check the position */
369 if(pHeader
->destination
!= reset_vector
) /* compare link addr. */
372 return eBadRomLink
; /* not matching the start address */
375 /* read the now following reset vector */
376 read
= rb
->read(fd
, &reset_vector
, sizeof(reset_vector
));
378 if (read
!= sizeof(reset_vector
))
385 pHeader
->execute
= reset_vector
;
388 /* check if we can read the whole file */
391 read
= rb
->read(fd
, sector
, SECTORSIZE
);
393 } while (read
== SECTORSIZE
);
397 if (fileread
!= filesize
)
404 /* returns the # of failures, 0 on success */
405 static unsigned ProgramImageFile(char* filename
, UINT8
* pos
,
406 tImageHeader
* pImageHeader
,
411 int read
; /* how many for this sector */
412 unsigned failures
= 0;
414 fd
= rb
->open(filename
, O_RDONLY
);
418 /* no error checking necessary here, we checked for minimum size
420 rb
->lseek(fd
, start
, SEEK_SET
); /* go to start position */
422 *(tImageHeader
*)sector
= *pImageHeader
; /* copy header into sector
424 read
= rb
->read(fd
, sector
+ sizeof(tImageHeader
),
425 SECTORSIZE
- sizeof(tImageHeader
)); /* payload behind */
427 read
+= sizeof(tImageHeader
); /* to be programmed, but not part of the
431 if (!EraseSector(pos
))
433 /* nothing we can do, let the programming count the errors */
436 for (i
=0; i
<read
; i
++)
438 if (!ProgramByte(pos
+ i
, sector
[i
]))
445 read
= rb
->read(fd
, sector
, (size
> SECTORSIZE
) ? SECTORSIZE
: size
);
446 /* payload for next sector */
455 /* returns the # of failures, 0 on success */
456 static unsigned VerifyImageFile(char* filename
, UINT8
* pos
,
457 tImageHeader
* pImageHeader
,
462 int read
; /* how many for this sector */
463 unsigned failures
= 0;
465 fd
= rb
->open(filename
, O_RDONLY
);
469 /* no error checking necessary here, we checked for minimum size
471 rb
->lseek(fd
, start
, SEEK_SET
); /* go to start position */
473 *(tImageHeader
*)sector
= *pImageHeader
; /* copy header into sector
475 read
= rb
->read(fd
, sector
+ sizeof(tImageHeader
),
476 SECTORSIZE
- sizeof(tImageHeader
)); /* payload behind */
479 read
+= sizeof(tImageHeader
); /* to be programmed, but not part of the
484 for (i
=0; i
<read
; i
++)
486 if (pos
[i
] != sector
[i
])
493 read
= rb
->read(fd
, sector
, (size
> SECTORSIZE
) ? SECTORSIZE
: size
);
494 /* payload for next sector */
504 /***************** User Interface Functions *****************/
506 static int WaitForButton(void)
512 button
= rb
->button_get(true);
513 } while (IS_SYSEVENT(button
) || (button
& BUTTON_REL
));
518 #ifdef HAVE_LCD_BITMAP
519 /* helper for DoUserDialog() */
520 static void ShowFlashInfo(tFlashInfo
* pInfo
, tImageHeader
* pImageHeader
)
524 if (!pInfo
->manufacturer
)
526 rb
->lcd_puts_scroll(0, 0, "Flash: M=?? D=??");
532 rb
->snprintf(buf
, sizeof(buf
), "Flash size: %d KB",
534 rb
->lcd_puts_scroll(0, 0, buf
);
538 rb
->lcd_puts_scroll(0, 0, "Unsupported chip");
545 rb
->snprintf(buf
, sizeof(buf
), "Image at %d KB",
546 ((UINT8
*)pImageHeader
- FB
) / 1024);
547 rb
->lcd_puts_scroll(0, 1, buf
);
551 rb
->lcd_puts_scroll(0, 1, "No image found!");
556 /* Kind of our main function, defines the application flow. */
557 /* recorder version */
558 static void DoUserDialog(char* filename
)
560 tImageHeader ImageHeader
;
561 tFlashInfo FlashInfo
;
563 int rc
; /* generic return code */
564 UINT32 space
, aligned_size
, true_size
;
568 bool show_greet
= false;
570 /* this can only work if Rockbox runs in DRAM, not flash ROM */
571 if ((UINT8
*)rb
>= FB
&& (UINT8
*)rb
< FB
+ 4096*1024) /* 4 MB max */
572 { /* we're running from flash */
573 rb
->splash(HZ
*3, "Not from ROM");
577 /* refuse to work if the power may fail meanwhile */
578 if (!rb
->battery_level_safe())
580 rb
->splash(HZ
*3, "Battery too low!");
584 /* "allocate" memory */
585 sector
= rb
->plugin_get_buffer(&memleft
);
586 if (memleft
< SECTORSIZE
) /* need buffer for a flash sector */
588 rb
->splash(HZ
*3, "Out of memory");
592 rb
->lcd_setfont(FONT_SYSFIXED
);
594 pos
= (void*)GetSecondImage();
595 rc
= GetFlashInfo(&FlashInfo
);
597 ShowFlashInfo(&FlashInfo
, (void*)pos
);
600 if (FlashInfo
.size
== 0) /* no valid chip */
602 rb
->splash(HZ
*3, "Not flashable");
607 rb
->splash(HZ
*3, "No image");
611 bl_version
= BootloaderVersion();
612 if (bl_version
< LATEST_BOOTLOADER_VERSION
)
614 rb
->lcd_putsf(0, 0, "Bootloader V%d", bl_version
);
615 rb
->lcd_puts(0, 1, "Hint: You're not ");
616 rb
->lcd_puts(0, 2, "using the latest ");
617 rb
->lcd_puts(0, 3, "bootloader. ");
618 rb
->lcd_puts(0, 4, "A full reflash is ");
619 rb
->lcd_puts(0, 5, "recommended, but ");
620 rb
->lcd_puts(0, 6, "not required. ");
621 rb
->lcd_puts(0, 7, "Press " KEYNAME1
" to ignore");
624 if (WaitForButton() != KEY1
)
628 rb
->lcd_clear_display();
631 rb
->lcd_puts(0, show_greet
? 0 : 3, "Checking...");
634 space
= FlashInfo
.size
- (pos
-FB
+ sizeof(ImageHeader
));
635 /* size minus start */
637 rc
= CheckImageFile(filename
, space
, &ImageHeader
, pos
);
640 rb
->lcd_clear_display(); /* make room for error message */
641 show_greet
= true; /* verbose */
644 rb
->lcd_puts(0, show_greet
? 0 : 3, "Checked:");
647 rb
->lcd_puts(0, show_greet
? 0 : 4, "File OK.");
650 rb
->lcd_puts(0, 1, "File not UCL ");
651 rb
->lcd_puts(0, 2, "compressed.");
652 rb
->lcd_puts(0, 3, "Use uclpack --2e");
653 rb
->lcd_puts(0, 4, " --10 rockbox.bin");
655 case eWrongAlgorithm
:
656 rb
->lcd_puts(0, 1, "Wrong algorithm");
657 rb
->lcd_puts(0, 2, "for compression.");
658 rb
->lcd_puts(0, 3, "Use uclpack --2e");
659 rb
->lcd_puts(0, 4, " --10 rockbox.bin");
662 rb
->lcd_puts(0, 1, "File not found:");
663 rb
->lcd_puts_scroll(0, 2, filename
);
666 rb
->lcd_puts(0, 1, "File too big,");
667 rb
->lcd_puts(0, 2, "won't fit in chip.");
670 rb
->lcd_puts(0, 1, "File too small.");
671 rb
->lcd_puts(0, 2, "Incomplete?");
674 rb
->lcd_puts(0, 1, "File read error.");
677 rb
->lcd_puts(0, 1, "File invalid.");
678 rb
->lcd_puts(0, 2, "Blocksize");
679 rb
->lcd_puts(0, 3, " too small?");
682 rb
->lcd_puts(0, 1, "RomBox mismatch.");
683 rb
->lcd_puts(0, 2, "Wrong ROM position");
686 rb
->lcd_puts(0, 1, "Check failed.");
692 rb
->lcd_puts(0, 6, "[" KEYNAME2
"] to program");
693 rb
->lcd_puts(0, 7, "other key to exit");
696 { /* error occured */
697 rb
->lcd_puts(0, 6, "Any key to exit");
701 button
= WaitForButton();
702 if (rc
!= eOK
|| button
!= KEY2
)
707 true_size
= ImageHeader
.size
;
708 aligned_size
= ((sizeof(tImageHeader
) + true_size
+ SECTORSIZE
-1) &
709 ~(SECTORSIZE
-1)) - sizeof(tImageHeader
); /* round up to
712 ImageHeader
.size
= aligned_size
; /* increase image size such that we reach
715 rb
->lcd_clear_display();
716 rb
->lcd_puts_scroll(0, 0, "Programming...");
719 rc
= ProgramImageFile(filename
, pos
, &ImageHeader
, UCL_HEADER
, true_size
);
722 rb
->lcd_clear_display();
723 rb
->lcd_puts(0, 0, "Error:");
724 rb
->lcd_puts(0, 1, "Programming fail!");
725 rb
->lcd_putsf(0, 2, "%d errors", rc
);
727 button
= WaitForButton();
730 rb
->lcd_clear_display();
731 rb
->lcd_puts_scroll(0, 0, "Verifying...");
734 rc
= VerifyImageFile(filename
, pos
, &ImageHeader
, UCL_HEADER
, true_size
);
736 rb
->lcd_clear_display();
739 rb
->lcd_puts(0, 0, "Verify OK.");
743 rb
->lcd_puts(0, 0, "Error:");
744 rb
->lcd_puts(0, 1, "Verify fail!");
745 rb
->lcd_putsf(0, 2, "%d errors", rc
);
746 rb
->lcd_puts(0, 3, "Use safe image");
747 rb
->lcd_puts(0, 4, "if booting hangs:");
748 rb
->lcd_puts(0, 5, "F1 during power-on");
750 rb
->lcd_puts(0, 7, "Any key to exit");
755 #else /* #ifdef HAVE_LCD_BITMAP */
758 static void DoUserDialog(char* filename
)
760 tImageHeader ImageHeader
;
761 tFlashInfo FlashInfo
;
762 static char buf
[MAX_PATH
];
764 int rc
; /* generic return code */
765 UINT32 space
, aligned_size
, true_size
;
770 /* this can only work if Rockbox runs in DRAM, not flash ROM */
771 if ((UINT8
*)rb
>= FB
&& (UINT8
*)rb
< FB
+ 4096*1024) /* 4 MB max */
772 { /* we're running from flash */
773 rb
->splash(HZ
*3, "Not from ROM");
777 /* refuse to work if the power may fail meanwhile */
778 if (!rb
->battery_level_safe())
780 rb
->splash(HZ
*3, "Batt. too low!");
784 /* "allocate" memory */
785 sector
= rb
->plugin_get_buffer(&memleft
);
786 if (memleft
< SECTORSIZE
) /* need buffer for a flash sector */
788 rb
->splash(HZ
*3, "Out of memory");
792 pos
= (void*)GetSecondImage();
793 rc
= GetFlashInfo(&FlashInfo
);
795 if (FlashInfo
.size
== 0) /* no valid chip */
797 rb
->splash(HZ
*3, "Not flashable");
802 rb
->splash(HZ
*3, "No image");
806 bl_version
= BootloaderVersion();
807 if (bl_version
< LATEST_BOOTLOADER_VERSION
)
809 rb
->lcd_puts_scroll(0, 0, "Hint: You're not using the latest bootloader. A full reflash is recommended, but not required.");
810 rb
->lcd_puts_scroll(0, 1, "Press [Menu] to ignore");
813 if (WaitForButton() != BUTTON_MENU
)
817 rb
->lcd_clear_display();
820 rb
->lcd_puts(0, 0, "Checking...");
823 space
= FlashInfo
.size
- (pos
-FB
+ sizeof(ImageHeader
));
824 /* size minus start */
826 rc
= CheckImageFile(filename
, space
, &ImageHeader
, pos
);
827 rb
->lcd_puts(0, 0, "Checked:");
830 rb
->lcd_puts(0, 1, "File OK.");
834 rb
->lcd_puts_scroll(0, 1, "File not UCL compressed.");
836 case eWrongAlgorithm
:
837 rb
->lcd_puts_scroll(0, 1, "Wrong compression algorithm.");
840 rb
->lcd_puts_scroll(0, 1, "File not found.");
843 rb
->lcd_puts_scroll(0, 1, "File too big.");
846 rb
->lcd_puts_scroll(0, 1, "File too small. Incomplete?");
849 rb
->lcd_puts_scroll(0, 1, "File read error.");
852 rb
->lcd_puts_scroll(0, 1, "File invalid. Blocksize too small?");
855 rb
->lcd_puts_scroll(0, 1, "RomBox mismatch.");
858 rb
->lcd_puts_scroll(0, 1, "Check failed.");
865 rb
->lcd_clear_display();
866 rb
->lcd_puts_scroll(0, 0, "[ON] to program,");
867 rb
->lcd_puts_scroll(0, 1, "other key to exit.");
870 { /* error occured */
872 rb
->lcd_clear_display();
873 rb
->lcd_puts_scroll(0, 0, "Flash failed.");
874 rb
->lcd_puts_scroll(0, 1, "Any key to exit.");
878 button
= WaitForButton();
879 if (rc
!= eOK
|| button
!= BUTTON_ON
)
884 true_size
= ImageHeader
.size
;
885 aligned_size
= ((sizeof(tImageHeader
) + true_size
+ SECTORSIZE
-1) &
886 ~(SECTORSIZE
-1)) - sizeof(tImageHeader
); /* round up to
889 ImageHeader
.size
= aligned_size
; /* increase image size such that we reach
892 rb
->lcd_clear_display();
893 rb
->lcd_puts_scroll(0, 0, "Programming...");
896 rc
= ProgramImageFile(filename
, pos
, &ImageHeader
, UCL_HEADER
, true_size
);
899 rb
->lcd_clear_display();
900 rb
->snprintf(buf
, sizeof(buf
), "%d errors", rc
);
901 rb
->lcd_puts_scroll(0, 0, "Programming failed!");
902 rb
->lcd_puts_scroll(0, 1, buf
);
904 button
= WaitForButton();
907 rb
->lcd_clear_display();
908 rb
->lcd_puts_scroll(0, 0, "Verifying...");
911 rc
= VerifyImageFile(filename
, pos
, &ImageHeader
, UCL_HEADER
, true_size
);
913 rb
->lcd_clear_display();
916 rb
->lcd_puts(0, 0, "Verify OK.");
921 rb
->snprintf(buf
, sizeof(buf
), "Verify fail! %d errors", rc
);
922 rb
->lcd_puts_scroll(0, 0, buf
);
923 rb
->lcd_puts_scroll(0, 1, "Use safe image if booting hangs: [-] during power-on");
925 button
= WaitForButton();
929 #endif /* not HAVE_LCD_BITMAP */
933 /***************** Plugin Entry Point *****************/
935 enum plugin_status
plugin_start(const void* parameter
)
939 if (parameter
== NULL
)
941 rb
->splash(HZ
*3, "Play .ucl file!");
945 /* now go ahead and have fun! */
946 oldmode
= rb
->system_memory_guard(MEMGUARD_NONE
); /*disable memory guard */
947 DoUserDialog((char*) parameter
);
948 rb
->system_memory_guard(oldmode
); /* re-enable memory guard */