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 #if (CONFIG_CPU == SH7034) /* Only for SH targets */
30 /* define DUMMY if you only want to "play" with the UI, does no harm */
33 #define LATEST_BOOTLOADER_VERSION 2 /* update this with the bootloader */
36 #define UINT8 unsigned char
40 #define UINT16 unsigned short
44 #define UINT32 unsigned long
47 /* hard-coded values */
48 static volatile UINT8
* FB
= (UINT8
*)0x02000000; /* Flash base address */
49 #define SECTORSIZE 4096 /* size of one flash sector */
51 #define ROCKBOX_DEST 0x09000000
52 #define ROCKBOX_EXEC 0x09000200
53 #define BOOT_VERS_ADR 0xFA /* position of bootloader version value in Flash */
54 #define FW_VERS_ADR 0xFE /* position of firmware version value in Flash */
55 #define UCL_HEADER 26 /* size of the header generated by uclpack */
57 #if CONFIG_KEYPAD == ONDIO_PAD /* limited keypad */
58 #define KEY1 BUTTON_LEFT
59 #define KEY2 BUTTON_UP
60 #define KEYNAME1 "Left"
62 #else /* recorder keypad */
63 #define KEY1 BUTTON_F1
64 #define KEY2 BUTTON_F2
71 UINT32 destination
; /* address to copy it to */
72 UINT32 size
; /* how many bytes of payload (to the next header) */
73 UINT32 execute
; /* entry point */
74 UINT32 flags
; /* uncompressed or compressed */
75 /* end of header, now comes the payload */
78 /* result of the CheckFirmwareFile() function */
82 eFileNotFound
, /* errors from here on */
100 static const struct plugin_api
* rb
; /* here is a global api struct pointer */
102 static UINT8
* sector
; /* better not place this on the stack... */
104 /***************** Flash Functions *****************/
107 /* read the manufacturer and device ID */
108 static bool ReadID(volatile UINT8
* pBase
, UINT8
* pManufacturerID
,
111 UINT8 not_manu
, not_id
; /* read values before switching to ID mode */
112 UINT8 manu
, id
; /* read values when in ID mode */
114 pBase
= (UINT8
*)((UINT32
)pBase
& 0xFFF80000); /* round down to 512k align,
117 not_manu
= pBase
[0]; /* read the normal content */
118 not_id
= pBase
[1]; /* should be 'A' (0x41) and 'R' (0x52) from the
121 pBase
[0x5555] = 0xAA; /* enter command mode */
122 pBase
[0x2AAA] = 0x55;
123 pBase
[0x5555] = 0x90; /* ID command */
124 rb
->sleep(HZ
/50); /* Atmel wants 20ms pause here */
129 pBase
[0] = 0xF0; /* reset flash (back to normal read mode) */
130 rb
->sleep(HZ
/50); /* Atmel wants 20ms pause here */
132 /* I assume success if the obtained values are different from
133 the normal flash content. This is not perfectly bulletproof, they
134 could theoretically be the same by chance, causing us to fail. */
135 if (not_manu
!= manu
|| not_id
!= id
) /* a value has changed */
137 *pManufacturerID
= manu
; /* return the results */
139 return true; /* success */
141 return false; /* fail */
144 /* erase the sector which contains the given address */
145 static bool EraseSector(volatile UINT8
* pAddr
)
148 (void)pAddr
; /* prevents warning */
151 volatile UINT8
* pBase
=
152 (UINT8
*)((UINT32
)pAddr
& 0xFFF80000); /* round down to 512k align */
153 unsigned timeout
= 43000; /* the timeout loop should be no less than
156 pBase
[0x5555] = 0xAA; /* enter command mode */
157 pBase
[0x2AAA] = 0x55;
158 pBase
[0x5555] = 0x80; /* erase command */
159 pBase
[0x5555] = 0xAA; /* enter command mode */
160 pBase
[0x2AAA] = 0x55;
161 *pAddr
= 0x30; /* erase the sector */
163 /* I counted 7 instructions for this loop -> min. 0.58 us per round
164 Plus memory waitstates it will be much more, gives margin */
165 while (*pAddr
!= 0xFF && --timeout
); /* poll for erased */
167 return (timeout
!= 0);
171 /* address must be in an erased location */
172 static inline bool ProgramByte(volatile UINT8
* pAddr
, UINT8 data
)
175 (void)pAddr
; /* prevents warnings */
179 unsigned timeout
= 35; /* the timeout loop should be no less than 20us */
181 if (~*pAddr
& data
) /* just a safety feature, not really necessary */
182 return false; /* can't set any bit from 0 to 1 */
184 FB
[0x5555] = 0xAA; /* enter command mode */
186 FB
[0x5555] = 0xA0; /* byte program command */
190 /* I counted 7 instructions for this loop -> min. 0.58 us per round
191 Plus memory waitstates it will be much more, gives margin */
192 while (*pAddr
!= data
&& --timeout
); /* poll for programmed */
194 return (timeout
!= 0);
198 /* this returns true if supported and fills the info struct */
199 static bool GetFlashInfo(tFlashInfo
* pInfo
)
201 rb
->memset(pInfo
, 0, sizeof(tFlashInfo
));
203 if (!ReadID(FB
, &pInfo
->manufacturer
, &pInfo
->id
))
206 if (pInfo
->manufacturer
== 0xBF) /* SST */
208 if (pInfo
->id
== 0xD6)
210 pInfo
->size
= 256* 1024; /* 256k */
211 rb
->strcpy(pInfo
->name
, "SST39VF020");
214 else if (pInfo
->id
== 0xD7)
216 pInfo
->size
= 512* 1024; /* 512k */
217 rb
->strcpy(pInfo
->name
, "SST39VF040");
227 /*********** Tool Functions ************/
229 /* read a 32 bit value from memory, big endian */
230 static UINT32
Read32(UINT8
* pByte
)
234 value
= (UINT32
)pByte
[0] << 24;
235 value
|= (UINT32
)pByte
[1] << 16;
236 value
|= (UINT32
)pByte
[2] << 8;
237 value
|= (UINT32
)pByte
[3];
242 /* get the start address of the second image */
243 static tImageHeader
* GetSecondImage(void)
245 tImageHeader
* pImage1
;
246 UINT32 pos
= 0; /* default: not found */
247 UINT32
* pFlash
= (UINT32
*)FB
;
249 /* determine the first image position */
250 pos
= pFlash
[2] + pFlash
[3]; /* position + size of the bootloader
252 pos
= (pos
+ 3) & ~3; /* be sure it's 32 bit aligned */
253 pImage1
= (tImageHeader
*)pos
;
255 if (pImage1
->destination
!= ROCKBOX_DEST
||
256 pImage1
->execute
!= ROCKBOX_EXEC
)
257 return 0; /* seems to be no Archos/Rockbox image in here */
259 if (pImage1
->size
!= 0)
261 /* success, we have a second image */
262 pos
= (UINT32
)pImage1
+ sizeof(tImageHeader
) + pImage1
->size
;
263 if (((pos
+ SECTORSIZE
-1) & ~(SECTORSIZE
-1)) != pos
)
264 { /* not sector-aligned */
265 pos
= 0; /* sanity check failed */
269 return (tImageHeader
*)pos
;
272 /* return bootloader version */
273 static inline unsigned BootloaderVersion(void)
275 return FB
[BOOT_VERS_ADR
];
278 /*********** Image File Functions ************/
280 /* so far, only compressed images in UCL NRV algorithm 2e supported */
281 tCheckResult
CheckImageFile(char* filename
, int space
,
282 tImageHeader
* pHeader
, UINT8
* pos
)
286 int filesize
; /* size info */
288 int fileread
= 0; /* total size as read from the file */
289 int read
; /* how many for this sector */
291 /* magic file header for compressed files */
292 static const UINT8 magic
[8] = { 0x00,0xe9,0x55,0x43,0x4c,0xff,0x01,0x1a };
293 UINT8 ucl_header
[UCL_HEADER
];
295 fd
= rb
->open(filename
, O_RDONLY
);
297 return eFileNotFound
;
299 filesize
= rb
->filesize(fd
);
300 if (filesize
- (int)sizeof(ucl_header
) - 8 > space
)
305 else if (filesize
< 10000) /* give it some reasonable lower limit */
311 /* do some sanity checks */
313 read
= rb
->read(fd
, ucl_header
, sizeof(ucl_header
));
315 if (read
!= sizeof(ucl_header
))
321 /* compare the magic header */
324 if (ucl_header
[i
] != magic
[i
])
331 pHeader
->size
= Read32(ucl_header
+ 22); /* compressed size */
332 if (pHeader
->size
!= filesize
- sizeof(ucl_header
) - 8)
338 /* fill in the hardcoded defaults of the header */
339 pHeader
->destination
= ROCKBOX_DEST
;
340 pHeader
->execute
= ROCKBOX_EXEC
;
342 if (Read32(ucl_header
+ 18) > pHeader
->size
) /* compare with uncompressed
344 { /* compressed, normal case */
345 pHeader
->flags
= 0x00000001; /* flags for UCL compressed */
347 /* check for supported algorithm */
348 if (ucl_header
[12] != 0x2E)
351 return eWrongAlgorithm
;
355 { /* uncompressed, either to be copied or run directly in flash */
356 UINT32 reset_vector
; /* image has to start with reset vector */
358 pHeader
->flags
= 0x00000000; /* uncompressed */
360 read
= rb
->read(fd
, &reset_vector
, sizeof(reset_vector
));
362 if (read
!= sizeof(reset_vector
))
367 if (reset_vector
>= (UINT32
)FB
368 && reset_vector
< (UINT32
)FB
+512*1024) /* ROM address? */
370 /* assume in-place, executing directly in flash */
371 pHeader
->destination
= (UINT32
)(pos
+ sizeof(tImageHeader
));
373 /* for new RomBox, this isn't the reset vector,
374 but the link address, for us to check the position */
375 if(pHeader
->destination
!= reset_vector
) /* compare link addr. */
378 return eBadRomLink
; /* not matching the start address */
381 /* read the now following reset vector */
382 read
= rb
->read(fd
, &reset_vector
, sizeof(reset_vector
));
384 if (read
!= sizeof(reset_vector
))
391 pHeader
->execute
= reset_vector
;
394 /* check if we can read the whole file */
397 read
= rb
->read(fd
, sector
, SECTORSIZE
);
399 } while (read
== SECTORSIZE
);
403 if (fileread
!= filesize
)
410 /* returns the # of failures, 0 on success */
411 static unsigned ProgramImageFile(char* filename
, UINT8
* pos
,
412 tImageHeader
* pImageHeader
,
417 int read
; /* how many for this sector */
418 unsigned failures
= 0;
420 fd
= rb
->open(filename
, O_RDONLY
);
424 /* no error checking necessary here, we checked for minimum size
426 rb
->lseek(fd
, start
, SEEK_SET
); /* go to start position */
428 *(tImageHeader
*)sector
= *pImageHeader
; /* copy header into sector
430 read
= rb
->read(fd
, sector
+ sizeof(tImageHeader
),
431 SECTORSIZE
- sizeof(tImageHeader
)); /* payload behind */
433 read
+= sizeof(tImageHeader
); /* to be programmed, but not part of the
437 if (!EraseSector(pos
))
439 /* nothing we can do, let the programming count the errors */
442 for (i
=0; i
<read
; i
++)
444 if (!ProgramByte(pos
+ i
, sector
[i
]))
451 read
= rb
->read(fd
, sector
, (size
> SECTORSIZE
) ? SECTORSIZE
: size
);
452 /* payload for next sector */
461 /* returns the # of failures, 0 on success */
462 static unsigned VerifyImageFile(char* filename
, UINT8
* pos
,
463 tImageHeader
* pImageHeader
,
468 int read
; /* how many for this sector */
469 unsigned failures
= 0;
471 fd
= rb
->open(filename
, O_RDONLY
);
475 /* no error checking necessary here, we checked for minimum size
477 rb
->lseek(fd
, start
, SEEK_SET
); /* go to start position */
479 *(tImageHeader
*)sector
= *pImageHeader
; /* copy header into sector
481 read
= rb
->read(fd
, sector
+ sizeof(tImageHeader
),
482 SECTORSIZE
- sizeof(tImageHeader
)); /* payload behind */
485 read
+= sizeof(tImageHeader
); /* to be programmed, but not part of the
490 for (i
=0; i
<read
; i
++)
492 if (pos
[i
] != sector
[i
])
499 read
= rb
->read(fd
, sector
, (size
> SECTORSIZE
) ? SECTORSIZE
: size
);
500 /* payload for next sector */
510 /***************** User Interface Functions *****************/
512 static int WaitForButton(void)
518 button
= rb
->button_get(true);
519 } while (button
& BUTTON_REL
);
524 #ifdef HAVE_LCD_BITMAP
525 /* helper for DoUserDialog() */
526 static void ShowFlashInfo(tFlashInfo
* pInfo
, tImageHeader
* pImageHeader
)
530 if (!pInfo
->manufacturer
)
532 rb
->lcd_puts_scroll(0, 0, "Flash: M=?? D=??");
538 rb
->snprintf(buf
, sizeof(buf
), "Flash size: %d KB",
540 rb
->lcd_puts_scroll(0, 0, buf
);
544 rb
->lcd_puts_scroll(0, 0, "Unsupported chip");
551 rb
->snprintf(buf
, sizeof(buf
), "Image at %d KB",
552 ((UINT8
*)pImageHeader
- FB
) / 1024);
553 rb
->lcd_puts_scroll(0, 1, buf
);
557 rb
->lcd_puts_scroll(0, 1, "No image found!");
562 /* Kind of our main function, defines the application flow. */
563 /* recorder version */
564 static void DoUserDialog(char* filename
)
566 tImageHeader ImageHeader
;
567 tFlashInfo FlashInfo
;
568 static char buf
[MAX_PATH
];
570 int rc
; /* generic return code */
571 UINT32 space
, aligned_size
, true_size
;
575 bool show_greet
= false;
577 /* this can only work if Rockbox runs in DRAM, not flash ROM */
578 if ((UINT8
*)rb
>= FB
&& (UINT8
*)rb
< FB
+ 4096*1024) /* 4 MB max */
579 { /* we're running from flash */
580 rb
->splash(HZ
*3, "Not from ROM");
584 /* refuse to work if the power may fail meanwhile */
585 if (!rb
->battery_level_safe())
587 rb
->splash(HZ
*3, "Battery too low!");
591 /* "allocate" memory */
592 sector
= rb
->plugin_get_buffer((size_t *)&memleft
);
593 if (memleft
< SECTORSIZE
) /* need buffer for a flash sector */
595 rb
->splash(HZ
*3, "Out of memory");
599 rb
->lcd_setfont(FONT_SYSFIXED
);
601 pos
= (void*)GetSecondImage();
602 rc
= GetFlashInfo(&FlashInfo
);
604 ShowFlashInfo(&FlashInfo
, (void*)pos
);
607 if (FlashInfo
.size
== 0) /* no valid chip */
609 rb
->splash(HZ
*3, "Not flashable");
614 rb
->splash(HZ
*3, "No image");
618 bl_version
= BootloaderVersion();
619 if (bl_version
< LATEST_BOOTLOADER_VERSION
)
621 rb
->snprintf(buf
, sizeof(buf
), "Bootloader V%d", bl_version
);
622 rb
->lcd_puts(0, 0, buf
);
623 rb
->lcd_puts(0, 1, "Hint: You're not ");
624 rb
->lcd_puts(0, 2, "using the latest ");
625 rb
->lcd_puts(0, 3, "bootloader. ");
626 rb
->lcd_puts(0, 4, "A full reflash is ");
627 rb
->lcd_puts(0, 5, "recommended, but ");
628 rb
->lcd_puts(0, 6, "not required. ");
629 rb
->lcd_puts(0, 7, "Press " KEYNAME1
" to ignore");
632 if (WaitForButton() != KEY1
)
636 rb
->lcd_clear_display();
639 rb
->lcd_puts(0, show_greet
? 0 : 3, "Checking...");
642 space
= FlashInfo
.size
- (pos
-FB
+ sizeof(ImageHeader
));
643 /* size minus start */
645 rc
= CheckImageFile(filename
, space
, &ImageHeader
, pos
);
648 rb
->lcd_clear_display(); /* make room for error message */
649 show_greet
= true; /* verbose */
652 rb
->lcd_puts(0, show_greet
? 0 : 3, "Checked:");
655 rb
->lcd_puts(0, show_greet
? 0 : 4, "File OK.");
658 rb
->lcd_puts(0, 1, "File not UCL ");
659 rb
->lcd_puts(0, 2, "compressed.");
660 rb
->lcd_puts(0, 3, "Use uclpack --2e");
661 rb
->lcd_puts(0, 4, " --10 rockbox.bin");
663 case eWrongAlgorithm
:
664 rb
->lcd_puts(0, 1, "Wrong algorithm");
665 rb
->lcd_puts(0, 2, "for compression.");
666 rb
->lcd_puts(0, 3, "Use uclpack --2e");
667 rb
->lcd_puts(0, 4, " --10 rockbox.bin");
670 rb
->lcd_puts(0, 1, "File not found:");
671 rb
->lcd_puts_scroll(0, 2, filename
);
674 rb
->lcd_puts(0, 1, "File too big,");
675 rb
->lcd_puts(0, 2, "won't fit in chip.");
678 rb
->lcd_puts(0, 1, "File too small.");
679 rb
->lcd_puts(0, 2, "Incomplete?");
682 rb
->lcd_puts(0, 1, "File read error.");
685 rb
->lcd_puts(0, 1, "File invalid.");
686 rb
->lcd_puts(0, 2, "Blocksize");
687 rb
->lcd_puts(0, 3, " too small?");
690 rb
->lcd_puts(0, 1, "RomBox mismatch.");
691 rb
->lcd_puts(0, 2, "Wrong ROM position");
694 rb
->lcd_puts(0, 1, "Check failed.");
700 rb
->lcd_puts(0, 6, "[" KEYNAME2
"] to program");
701 rb
->lcd_puts(0, 7, "other key to exit");
704 { /* error occured */
705 rb
->lcd_puts(0, 6, "Any key to exit");
709 button
= WaitForButton();
710 if (rc
!= eOK
|| button
!= KEY2
)
715 true_size
= ImageHeader
.size
;
716 aligned_size
= ((sizeof(tImageHeader
) + true_size
+ SECTORSIZE
-1) &
717 ~(SECTORSIZE
-1)) - sizeof(tImageHeader
); /* round up to
720 ImageHeader
.size
= aligned_size
; /* increase image size such that we reach
723 rb
->lcd_clear_display();
724 rb
->lcd_puts_scroll(0, 0, "Programming...");
727 rc
= ProgramImageFile(filename
, pos
, &ImageHeader
, UCL_HEADER
, true_size
);
730 rb
->lcd_clear_display();
731 rb
->snprintf(buf
, sizeof(buf
), "%d errors", rc
);
732 rb
->lcd_puts(0, 0, "Error:");
733 rb
->lcd_puts(0, 1, "Programming fail!");
734 rb
->lcd_puts(0, 2, buf
);
736 button
= WaitForButton();
739 rb
->lcd_clear_display();
740 rb
->lcd_puts_scroll(0, 0, "Verifying...");
743 rc
= VerifyImageFile(filename
, pos
, &ImageHeader
, UCL_HEADER
, true_size
);
745 rb
->lcd_clear_display();
748 rb
->lcd_puts(0, 0, "Verify OK.");
752 rb
->snprintf(buf
, sizeof(buf
), "%d errors", rc
);
753 rb
->lcd_puts(0, 0, "Error:");
754 rb
->lcd_puts(0, 1, "Verify fail!");
755 rb
->lcd_puts(0, 2, buf
);
756 rb
->lcd_puts(0, 3, "Use safe image");
757 rb
->lcd_puts(0, 4, "if booting hangs:");
758 rb
->lcd_puts(0, 5, "F1 during power-on");
760 rb
->lcd_puts(0, 7, "Any key to exit");
765 #else /* #ifdef HAVE_LCD_BITMAP */
768 static void DoUserDialog(char* filename
)
770 tImageHeader ImageHeader
;
771 tFlashInfo FlashInfo
;
772 static char buf
[MAX_PATH
];
774 int rc
; /* generic return code */
775 UINT32 space
, aligned_size
, true_size
;
780 /* this can only work if Rockbox runs in DRAM, not flash ROM */
781 if ((UINT8
*)rb
>= FB
&& (UINT8
*)rb
< FB
+ 4096*1024) /* 4 MB max */
782 { /* we're running from flash */
783 rb
->splash(HZ
*3, "Not from ROM");
787 /* refuse to work if the power may fail meanwhile */
788 if (!rb
->battery_level_safe())
790 rb
->splash(HZ
*3, "Batt. too low!");
794 /* "allocate" memory */
795 sector
= rb
->plugin_get_buffer((size_t *)&memleft
);
796 if (memleft
< SECTORSIZE
) /* need buffer for a flash sector */
798 rb
->splash(HZ
*3, "Out of memory");
802 pos
= (void*)GetSecondImage();
803 rc
= GetFlashInfo(&FlashInfo
);
805 if (FlashInfo
.size
== 0) /* no valid chip */
807 rb
->splash(HZ
*3, "Not flashable");
812 rb
->splash(HZ
*3, "No image");
816 bl_version
= BootloaderVersion();
817 if (bl_version
< LATEST_BOOTLOADER_VERSION
)
819 rb
->lcd_puts_scroll(0, 0, "Hint: You're not using the latest bootloader. A full reflash is recommended, but not required.");
820 rb
->lcd_puts_scroll(0, 1, "Press [Menu] to ignore");
823 if (WaitForButton() != BUTTON_MENU
)
827 rb
->lcd_clear_display();
830 rb
->lcd_puts(0, 0, "Checking...");
833 space
= FlashInfo
.size
- (pos
-FB
+ sizeof(ImageHeader
));
834 /* size minus start */
836 rc
= CheckImageFile(filename
, space
, &ImageHeader
, pos
);
837 rb
->lcd_puts(0, 0, "Checked:");
840 rb
->lcd_puts(0, 1, "File OK.");
844 rb
->lcd_puts_scroll(0, 1, "File not UCL compressed.");
846 case eWrongAlgorithm
:
847 rb
->lcd_puts_scroll(0, 1, "Wrong compression algorithm.");
850 rb
->lcd_puts_scroll(0, 1, "File not found.");
853 rb
->lcd_puts_scroll(0, 1, "File too big.");
856 rb
->lcd_puts_scroll(0, 1, "File too small. Incomplete?");
859 rb
->lcd_puts_scroll(0, 1, "File read error.");
862 rb
->lcd_puts_scroll(0, 1, "File invalid. Blocksize too small?");
865 rb
->lcd_puts_scroll(0, 1, "RomBox mismatch.");
868 rb
->lcd_puts_scroll(0, 1, "Check failed.");
875 rb
->lcd_clear_display();
876 rb
->lcd_puts_scroll(0, 0, "[ON] to program,");
877 rb
->lcd_puts_scroll(0, 1, "other key to exit.");
880 { /* error occured */
882 rb
->lcd_clear_display();
883 rb
->lcd_puts_scroll(0, 0, "Flash failed.");
884 rb
->lcd_puts_scroll(0, 1, "Any key to exit.");
888 button
= WaitForButton();
889 if (rc
!= eOK
|| button
!= BUTTON_ON
)
894 true_size
= ImageHeader
.size
;
895 aligned_size
= ((sizeof(tImageHeader
) + true_size
+ SECTORSIZE
-1) &
896 ~(SECTORSIZE
-1)) - sizeof(tImageHeader
); /* round up to
899 ImageHeader
.size
= aligned_size
; /* increase image size such that we reach
902 rb
->lcd_clear_display();
903 rb
->lcd_puts_scroll(0, 0, "Programming...");
906 rc
= ProgramImageFile(filename
, pos
, &ImageHeader
, UCL_HEADER
, true_size
);
909 rb
->lcd_clear_display();
910 rb
->snprintf(buf
, sizeof(buf
), "%d errors", rc
);
911 rb
->lcd_puts_scroll(0, 0, "Programming failed!");
912 rb
->lcd_puts_scroll(0, 1, buf
);
914 button
= WaitForButton();
917 rb
->lcd_clear_display();
918 rb
->lcd_puts_scroll(0, 0, "Verifying...");
921 rc
= VerifyImageFile(filename
, pos
, &ImageHeader
, UCL_HEADER
, true_size
);
923 rb
->lcd_clear_display();
926 rb
->lcd_puts(0, 0, "Verify OK.");
931 rb
->snprintf(buf
, sizeof(buf
), "Verify fail! %d errors", rc
);
932 rb
->lcd_puts_scroll(0, 0, buf
);
933 rb
->lcd_puts_scroll(0, 1, "Use safe image if booting hangs: [-] during power-on");
935 button
= WaitForButton();
939 #endif /* not HAVE_LCD_BITMAP */
943 /***************** Plugin Entry Point *****************/
945 enum plugin_status
plugin_start(const struct plugin_api
* api
, const void* parameter
)
949 rb
= api
; /* copy to global api pointer */
951 if (parameter
== NULL
)
953 rb
->splash(HZ
*3, "Play .ucl file!");
957 /* now go ahead and have fun! */
958 oldmode
= rb
->system_memory_guard(MEMGUARD_NONE
); /*disable memory guard */
959 DoUserDialog((char*) parameter
);
960 rb
->system_memory_guard(oldmode
); /* re-enable memory guard */
966 #endif /* SH-target */