FS#12076 - DB stats resurrection: If the filename was changed, require
[kugel-rb.git] / apps / plugins / rockbox_flash.c
blob09ee96997ca4b5c175ce3bc9bda8567b4fe8234f
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
24 #include "plugin.h"
26 /* define DUMMY if you only want to "play" with the UI, does no harm */
27 /* #define DUMMY */
29 #define LATEST_BOOTLOADER_VERSION 3 /* update this with the bootloader */
31 #ifndef UINT8
32 #define UINT8 unsigned char
33 #endif
35 #ifndef UINT16
36 #define UINT16 unsigned short
37 #endif
39 #ifndef UINT32
40 #define UINT32 unsigned long
41 #endif
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"
57 #define KEYNAME2 "Up"
58 #else /* recorder keypad */
59 #define KEY1 BUTTON_F1
60 #define KEY2 BUTTON_F2
61 #define KEYNAME1 "F1"
62 #define KEYNAME2 "F2"
63 #endif
65 typedef struct
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 */
72 } tImageHeader;
74 /* result of the CheckFirmwareFile() function */
75 typedef enum
77 eOK = 0,
78 eFileNotFound, /* errors from here on */
79 eTooBig,
80 eTooSmall,
81 eReadErr,
82 eNotUCL,
83 eWrongAlgorithm,
84 eMultiBlocks,
85 eBadRomLink
86 } tCheckResult;
88 typedef struct
90 UINT8 manufacturer;
91 UINT8 id;
92 int size;
93 char name[32];
94 } tFlashInfo;
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,
103 UINT8* pDeviceID)
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,
109 to make sure */
111 not_manu = pBase[0]; /* read the normal content */
112 not_id = pBase[1]; /* should be 'A' (0x41) and 'R' (0x52) from the
113 "ARCH" marker */
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 */
120 manu = pBase[0];
121 id = pBase[1];
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 */
132 *pDeviceID = id;
133 return true; /* success */
135 return false; /* fail */
138 /* erase the sector which contains the given address */
139 static bool EraseSector(volatile UINT8* pAddr)
141 #ifdef DUMMY
142 (void)pAddr; /* prevents warning */
143 return true;
144 #else
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
148 25ms */
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);
162 #endif
165 /* address must be in an erased location */
166 static inline bool ProgramByte(volatile UINT8* pAddr, UINT8 data)
168 #ifdef DUMMY
169 (void)pAddr; /* prevents warnings */
170 (void)data;
171 return true;
172 #else
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 */
179 FB[0x2AAA] = 0x55;
180 FB[0x5555] = 0xA0; /* byte program command */
182 *pAddr = data;
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);
189 #endif
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))
198 return false;
200 if (pInfo->manufacturer == 0xBF) /* SST */
202 if (pInfo->id == 0xD6)
204 pInfo->size = 256* 1024; /* 256k */
205 rb->strcpy(pInfo->name, "SST39VF020");
206 return true;
208 else if (pInfo->id == 0xD7)
210 pInfo->size = 512* 1024; /* 512k */
211 rb->strcpy(pInfo->name, "SST39VF040");
212 return true;
214 else
215 return false;
217 return false;
221 /*********** Tool Functions ************/
223 /* read a 32 bit value from memory, big endian */
224 static UINT32 Read32(UINT8* pByte)
226 UINT32 value;
228 value = (UINT32)pByte[0] << 24;
229 value |= (UINT32)pByte[1] << 16;
230 value |= (UINT32)pByte[2] << 8;
231 value |= (UINT32)pByte[3];
233 return value;
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
245 = after it */
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)
278 int i;
279 int fd;
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);
290 if (fd < 0)
291 return eFileNotFound;
293 filesize = rb->filesize(fd);
294 if (filesize - (int)sizeof(ucl_header) - 8 > space)
296 rb->close(fd);
297 return eTooBig;
299 else if (filesize < 10000) /* give it some reasonable lower limit */
301 rb->close(fd);
302 return eTooSmall;
305 /* do some sanity checks */
307 read = rb->read(fd, ucl_header, sizeof(ucl_header));
308 fileread += read;
309 if (read != sizeof(ucl_header))
311 rb->close(fd);
312 return eReadErr;
315 /* compare the magic header */
316 for (i=0; i<8; i++)
318 if (ucl_header[i] != magic[i])
320 rb->close(fd);
321 return eNotUCL;
325 pHeader->size = Read32(ucl_header + 22); /* compressed size */
326 if (pHeader->size != filesize - sizeof(ucl_header) - 8)
328 rb->close(fd);
329 return eMultiBlocks;
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
337 size */
338 { /* compressed, normal case */
339 pHeader->flags = 0x00000001; /* flags for UCL compressed */
341 /* check for supported algorithm */
342 if (ucl_header[12] != 0x2E)
344 rb->close(fd);
345 return eWrongAlgorithm;
348 else
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));
355 fileread += read;
356 if (read != sizeof(reset_vector))
358 rb->close(fd);
359 return eReadErr;
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. */
371 rb->close(fd);
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));
377 fileread += read;
378 if (read != sizeof(reset_vector))
380 rb->close(fd);
381 return eReadErr;
385 pHeader->execute = reset_vector;
388 /* check if we can read the whole file */
391 read = rb->read(fd, sector, SECTORSIZE);
392 fileread += read;
393 } while (read == SECTORSIZE);
395 rb->close(fd);
397 if (fileread != filesize)
398 return eReadErr;
400 return eOK;
404 /* returns the # of failures, 0 on success */
405 static unsigned ProgramImageFile(char* filename, UINT8* pos,
406 tImageHeader* pImageHeader,
407 int start, int size)
409 int i;
410 int fd;
411 int read; /* how many for this sector */
412 unsigned failures = 0;
414 fd = rb->open(filename, O_RDONLY);
415 if (fd < 0)
416 return false;
418 /* no error checking necessary here, we checked for minimum size
419 already */
420 rb->lseek(fd, start, SEEK_SET); /* go to start position */
422 *(tImageHeader*)sector = *pImageHeader; /* copy header into sector
423 buffer */
424 read = rb->read(fd, sector + sizeof(tImageHeader),
425 SECTORSIZE - sizeof(tImageHeader)); /* payload behind */
426 size -= read;
427 read += sizeof(tImageHeader); /* to be programmed, but not part of the
428 file */
430 do {
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]))
440 failures++;
444 pos += SECTORSIZE;
445 read = rb->read(fd, sector, (size > SECTORSIZE) ? SECTORSIZE : size);
446 /* payload for next sector */
447 size -= read;
448 } while (read > 0);
450 rb->close(fd);
452 return failures;
455 /* returns the # of failures, 0 on success */
456 static unsigned VerifyImageFile(char* filename, UINT8* pos,
457 tImageHeader* pImageHeader,
458 int start, int size)
460 int i;
461 int fd;
462 int read; /* how many for this sector */
463 unsigned failures = 0;
465 fd = rb->open(filename, O_RDONLY);
466 if (fd < 0)
467 return false;
469 /* no error checking necessary here, we checked for minimum size
470 already */
471 rb->lseek(fd, start, SEEK_SET); /* go to start position */
473 *(tImageHeader*)sector = *pImageHeader; /* copy header into sector
474 buffer */
475 read = rb->read(fd, sector + sizeof(tImageHeader),
476 SECTORSIZE - sizeof(tImageHeader)); /* payload behind */
478 size -= read;
479 read += sizeof(tImageHeader); /* to be programmed, but not part of the
480 file */
484 for (i=0; i<read; i++)
486 if (pos[i] != sector[i])
488 failures++;
492 pos += SECTORSIZE;
493 read = rb->read(fd, sector, (size > SECTORSIZE) ? SECTORSIZE : size);
494 /* payload for next sector */
495 size -= read;
496 } while (read);
498 rb->close(fd);
500 return failures;
504 /***************** User Interface Functions *****************/
506 static int WaitForButton(void)
508 int button;
512 button = rb->button_get(true);
513 } while (IS_SYSEVENT(button) || (button & BUTTON_REL));
515 return button;
518 #ifdef HAVE_LCD_BITMAP
519 /* helper for DoUserDialog() */
520 static void ShowFlashInfo(tFlashInfo* pInfo, tImageHeader* pImageHeader)
522 char buf[32];
524 if (!pInfo->manufacturer)
526 rb->lcd_puts_scroll(0, 0, "Flash: M=?? D=??");
528 else
530 if (pInfo->size)
532 rb->snprintf(buf, sizeof(buf), "Flash size: %d KB",
533 pInfo->size / 1024);
534 rb->lcd_puts_scroll(0, 0, buf);
536 else
538 rb->lcd_puts_scroll(0, 0, "Unsupported chip");
543 if (pImageHeader)
545 rb->snprintf(buf, sizeof(buf), "Image at %d KB",
546 ((UINT8*)pImageHeader - FB) / 1024);
547 rb->lcd_puts_scroll(0, 1, buf);
549 else
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;
562 int button;
563 int rc; /* generic return code */
564 UINT32 space, aligned_size, true_size;
565 UINT8* pos;
566 size_t memleft;
567 unsigned bl_version;
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");
574 return; /* exit */
577 /* refuse to work if the power may fail meanwhile */
578 if (!rb->battery_level_safe())
580 rb->splash(HZ*3, "Battery too low!");
581 return; /* exit */
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");
589 return; /* exit */
592 rb->lcd_setfont(FONT_SYSFIXED);
594 pos = (void*)GetSecondImage();
595 rc = GetFlashInfo(&FlashInfo);
597 ShowFlashInfo(&FlashInfo, (void*)pos);
598 rb->lcd_update();
600 if (FlashInfo.size == 0) /* no valid chip */
602 rb->splash(HZ*3, "Not flashable");
603 return; /* exit */
605 else if (pos == 0)
607 rb->splash(HZ*3, "No image");
608 return; /* exit */
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");
622 rb->lcd_update();
624 if (WaitForButton() != KEY1)
626 return;
628 rb->lcd_clear_display();
631 rb->lcd_puts(0, show_greet ? 0 : 3, "Checking...");
632 rb->lcd_update();
634 space = FlashInfo.size - (pos-FB + sizeof(ImageHeader));
635 /* size minus start */
637 rc = CheckImageFile(filename, space, &ImageHeader, pos);
638 if (rc != eOK)
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:");
645 switch (rc) {
646 case eOK:
647 rb->lcd_puts(0, show_greet ? 0 : 4, "File OK.");
648 break;
649 case eNotUCL:
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");
654 break;
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");
660 break;
661 case eFileNotFound:
662 rb->lcd_puts(0, 1, "File not found:");
663 rb->lcd_puts_scroll(0, 2, filename);
664 break;
665 case eTooBig:
666 rb->lcd_puts(0, 1, "File too big,");
667 rb->lcd_puts(0, 2, "won't fit in chip.");
668 break;
669 case eTooSmall:
670 rb->lcd_puts(0, 1, "File too small.");
671 rb->lcd_puts(0, 2, "Incomplete?");
672 break;
673 case eReadErr:
674 rb->lcd_puts(0, 1, "File read error.");
675 break;
676 case eMultiBlocks:
677 rb->lcd_puts(0, 1, "File invalid.");
678 rb->lcd_puts(0, 2, "Blocksize");
679 rb->lcd_puts(0, 3, " too small?");
680 break;
681 case eBadRomLink:
682 rb->lcd_puts(0, 1, "RomBox mismatch.");
683 rb->lcd_puts(0, 2, "Wrong ROM position");
684 break;
685 default:
686 rb->lcd_puts(0, 1, "Check failed.");
687 break;
690 if (rc == eOK)
691 { /* was OK */
692 rb->lcd_puts(0, 6, "[" KEYNAME2 "] to program");
693 rb->lcd_puts(0, 7, "other key to exit");
695 else
696 { /* error occured */
697 rb->lcd_puts(0, 6, "Any key to exit");
699 rb->lcd_update();
701 button = WaitForButton();
702 if (rc != eOK || button != KEY2)
704 return;
707 true_size = ImageHeader.size;
708 aligned_size = ((sizeof(tImageHeader) + true_size + SECTORSIZE-1) &
709 ~(SECTORSIZE-1)) - sizeof(tImageHeader); /* round up to
710 next flash
711 sector */
712 ImageHeader.size = aligned_size; /* increase image size such that we reach
713 the next sector */
715 rb->lcd_clear_display();
716 rb->lcd_puts_scroll(0, 0, "Programming...");
717 rb->lcd_update();
719 rc = ProgramImageFile(filename, pos, &ImageHeader, UCL_HEADER, true_size);
720 if (rc)
721 { /* errors */
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);
726 rb->lcd_update();
727 button = WaitForButton();
730 rb->lcd_clear_display();
731 rb->lcd_puts_scroll(0, 0, "Verifying...");
732 rb->lcd_update();
734 rc = VerifyImageFile(filename, pos, &ImageHeader, UCL_HEADER, true_size);
736 rb->lcd_clear_display();
737 if (rc == 0)
739 rb->lcd_puts(0, 0, "Verify OK.");
741 else
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");
751 rb->lcd_update();
752 WaitForButton();
755 #else /* #ifdef HAVE_LCD_BITMAP */
757 /* Player version */
758 static void DoUserDialog(char* filename)
760 tImageHeader ImageHeader;
761 tFlashInfo FlashInfo;
762 static char buf[MAX_PATH];
763 int button;
764 int rc; /* generic return code */
765 UINT32 space, aligned_size, true_size;
766 UINT8* pos;
767 size_t memleft;
768 unsigned bl_version;
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");
774 return; /* exit */
777 /* refuse to work if the power may fail meanwhile */
778 if (!rb->battery_level_safe())
780 rb->splash(HZ*3, "Batt. too low!");
781 return; /* exit */
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");
789 return; /* exit */
792 pos = (void*)GetSecondImage();
793 rc = GetFlashInfo(&FlashInfo);
795 if (FlashInfo.size == 0) /* no valid chip */
797 rb->splash(HZ*3, "Not flashable");
798 return; /* exit */
800 else if (pos == 0)
802 rb->splash(HZ*3, "No image");
803 return; /* exit */
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");
811 rb->lcd_update();
813 if (WaitForButton() != BUTTON_MENU)
815 return;
817 rb->lcd_clear_display();
820 rb->lcd_puts(0, 0, "Checking...");
821 rb->lcd_update();
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:");
828 switch (rc) {
829 case eOK:
830 rb->lcd_puts(0, 1, "File OK.");
831 rb->sleep(HZ*1);
832 break;
833 case eNotUCL:
834 rb->lcd_puts_scroll(0, 1, "File not UCL compressed.");
835 break;
836 case eWrongAlgorithm:
837 rb->lcd_puts_scroll(0, 1, "Wrong compression algorithm.");
838 break;
839 case eFileNotFound:
840 rb->lcd_puts_scroll(0, 1, "File not found.");
841 break;
842 case eTooBig:
843 rb->lcd_puts_scroll(0, 1, "File too big.");
844 break;
845 case eTooSmall:
846 rb->lcd_puts_scroll(0, 1, "File too small. Incomplete?");
847 break;
848 case eReadErr:
849 rb->lcd_puts_scroll(0, 1, "File read error.");
850 break;
851 case eMultiBlocks:
852 rb->lcd_puts_scroll(0, 1, "File invalid. Blocksize too small?");
853 break;
854 case eBadRomLink:
855 rb->lcd_puts_scroll(0, 1, "RomBox mismatch.");
856 break;
857 default:
858 rb->lcd_puts_scroll(0, 1, "Check failed.");
859 break;
861 rb->lcd_update();
863 if (rc == eOK)
864 { /* was OK */
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.");
869 else
870 { /* error occured */
871 WaitForButton();
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.");
876 rb->lcd_update();
878 button = WaitForButton();
879 if (rc != eOK || button != BUTTON_ON)
881 return;
884 true_size = ImageHeader.size;
885 aligned_size = ((sizeof(tImageHeader) + true_size + SECTORSIZE-1) &
886 ~(SECTORSIZE-1)) - sizeof(tImageHeader); /* round up to
887 next flash
888 sector */
889 ImageHeader.size = aligned_size; /* increase image size such that we reach
890 the next sector */
892 rb->lcd_clear_display();
893 rb->lcd_puts_scroll(0, 0, "Programming...");
894 rb->lcd_update();
896 rc = ProgramImageFile(filename, pos, &ImageHeader, UCL_HEADER, true_size);
897 if (rc)
898 { /* errors */
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);
903 rb->lcd_update();
904 button = WaitForButton();
907 rb->lcd_clear_display();
908 rb->lcd_puts_scroll(0, 0, "Verifying...");
909 rb->lcd_update();
911 rc = VerifyImageFile(filename, pos, &ImageHeader, UCL_HEADER, true_size);
913 rb->lcd_clear_display();
914 if (rc == 0)
916 rb->lcd_puts(0, 0, "Verify OK.");
917 rb->lcd_update();
919 else
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");
924 rb->lcd_update();
925 button = WaitForButton();
929 #endif /* not HAVE_LCD_BITMAP */
933 /***************** Plugin Entry Point *****************/
935 enum plugin_status plugin_start(const void* parameter)
937 int oldmode;
939 if (parameter == NULL)
941 rb->splash(HZ*3, "Play .ucl file!");
942 return PLUGIN_OK;
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 */
950 return PLUGIN_OK;