Rename variables sectorbuf and verbose to avoid clashes in rbutil. Cleanup exports...
[Rockbox.git] / apps / plugins / rockbox_flash.c
blobfd855ff5209ea3082fa2bbb416d8a40d86d58733
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 * All files in this archive are subject to the GNU General Public License.
16 * See the file COPYING in the source tree root for full license agreement.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
22 #include "plugin.h"
24 #if (CONFIG_CPU == SH7034) /* Only for SH targets */
26 PLUGIN_HEADER
28 /* define DUMMY if you only want to "play" with the UI, does no harm */
29 /* #define DUMMY */
31 #define LATEST_BOOTLOADER_VERSION 1 /* update this with the bootloader */
33 #ifndef UINT8
34 #define UINT8 unsigned char
35 #endif
37 #ifndef UINT16
38 #define UINT16 unsigned short
39 #endif
41 #ifndef UINT32
42 #define UINT32 unsigned long
43 #endif
45 /* hard-coded values */
46 static volatile UINT8* FB = (UINT8*)0x02000000; /* Flash base address */
47 #define SECTORSIZE 4096 /* size of one flash sector */
49 #define ROCKBOX_DEST 0x09000000
50 #define ROCKBOX_EXEC 0x09000200
51 #define BOOT_VERS_ADR 0xFA /* position of bootloader version value in Flash */
52 #define FW_VERS_ADR 0xFE /* position of firmware version value in Flash */
53 #define UCL_HEADER 26 /* size of the header generated by uclpack */
55 #if CONFIG_KEYPAD == ONDIO_PAD /* limited keypad */
56 #define KEY1 BUTTON_LEFT
57 #define KEY2 BUTTON_UP
58 #define KEYNAME1 "Left"
59 #define KEYNAME2 "Up"
60 #else /* recorder keypad */
61 #define KEY1 BUTTON_F1
62 #define KEY2 BUTTON_F2
63 #define KEYNAME1 "F1"
64 #define KEYNAME2 "F2"
65 #endif
67 typedef struct
69 UINT32 destination; /* address to copy it to */
70 UINT32 size; /* how many bytes of payload (to the next header) */
71 UINT32 execute; /* entry point */
72 UINT32 flags; /* uncompressed or compressed */
73 /* end of header, now comes the payload */
74 } tImageHeader;
76 /* result of the CheckFirmwareFile() function */
77 typedef enum
79 eOK = 0,
80 eFileNotFound, /* errors from here on */
81 eTooBig,
82 eTooSmall,
83 eReadErr,
84 eNotUCL,
85 eWrongAlgorithm,
86 eMultiBlocks,
87 eBadRomLink
88 } tCheckResult;
90 typedef struct
92 UINT8 manufacturer;
93 UINT8 id;
94 int size;
95 char name[32];
96 } tFlashInfo;
98 static const struct plugin_api* rb; /* here is a global api struct pointer */
100 static UINT8* sector; /* better not place this on the stack... */
102 /***************** Flash Functions *****************/
105 /* read the manufacturer and device ID */
106 bool ReadID(volatile UINT8* pBase, UINT8* pManufacturerID, UINT8* pDeviceID)
108 UINT8 not_manu, not_id; /* read values before switching to ID mode */
109 UINT8 manu, id; /* read values when in ID mode */
111 pBase = (UINT8*)((UINT32)pBase & 0xFFF80000); /* round down to 512k align,
112 to make sure */
114 not_manu = pBase[0]; /* read the normal content */
115 not_id = pBase[1]; /* should be 'A' (0x41) and 'R' (0x52) from the
116 "ARCH" marker */
118 pBase[0x5555] = 0xAA; /* enter command mode */
119 pBase[0x2AAA] = 0x55;
120 pBase[0x5555] = 0x90; /* ID command */
121 rb->sleep(HZ/50); /* Atmel wants 20ms pause here */
123 manu = pBase[0];
124 id = pBase[1];
126 pBase[0] = 0xF0; /* reset flash (back to normal read mode) */
127 rb->sleep(HZ/50); /* Atmel wants 20ms pause here */
129 /* I assume success if the obtained values are different from
130 the normal flash content. This is not perfectly bulletproof, they
131 could theoretically be the same by chance, causing us to fail. */
132 if (not_manu != manu || not_id != id) /* a value has changed */
134 *pManufacturerID = manu; /* return the results */
135 *pDeviceID = id;
136 return true; /* success */
138 return false; /* fail */
141 /* erase the sector which contains the given address */
142 bool EraseSector(volatile UINT8* pAddr)
144 #ifdef DUMMY
145 (void)pAddr; /* prevents warning */
146 return true;
147 #else
148 volatile UINT8* pBase =
149 (UINT8*)((UINT32)pAddr & 0xFFF80000); /* round down to 512k align */
150 unsigned timeout = 43000; /* the timeout loop should be no less than
151 25ms */
153 pBase[0x5555] = 0xAA; /* enter command mode */
154 pBase[0x2AAA] = 0x55;
155 pBase[0x5555] = 0x80; /* erase command */
156 pBase[0x5555] = 0xAA; /* enter command mode */
157 pBase[0x2AAA] = 0x55;
158 *pAddr = 0x30; /* erase the sector */
160 /* I counted 7 instructions for this loop -> min. 0.58 us per round
161 Plus memory waitstates it will be much more, gives margin */
162 while (*pAddr != 0xFF && --timeout); /* poll for erased */
164 return (timeout != 0);
165 #endif
168 /* address must be in an erased location */
169 inline bool ProgramByte(volatile UINT8* pAddr, UINT8 data)
171 #ifdef DUMMY
172 (void)pAddr; /* prevents warnings */
173 (void)data;
174 return true;
175 #else
176 unsigned timeout = 35; /* the timeout loop should be no less than 20us */
178 if (~*pAddr & data) /* just a safety feature, not really necessary */
179 return false; /* can't set any bit from 0 to 1 */
181 FB[0x5555] = 0xAA; /* enter command mode */
182 FB[0x2AAA] = 0x55;
183 FB[0x5555] = 0xA0; /* byte program command */
185 *pAddr = data;
187 /* I counted 7 instructions for this loop -> min. 0.58 us per round
188 Plus memory waitstates it will be much more, gives margin */
189 while (*pAddr != data && --timeout); /* poll for programmed */
191 return (timeout != 0);
192 #endif
195 /* this returns true if supported and fills the info struct */
196 bool GetFlashInfo(tFlashInfo* pInfo)
198 rb->memset(pInfo, 0, sizeof(tFlashInfo));
200 if (!ReadID(FB, &pInfo->manufacturer, &pInfo->id))
201 return false;
203 if (pInfo->manufacturer == 0xBF) /* SST */
205 if (pInfo->id == 0xD6)
207 pInfo->size = 256* 1024; /* 256k */
208 rb->strcpy(pInfo->name, "SST39VF020");
209 return true;
211 else if (pInfo->id == 0xD7)
213 pInfo->size = 512* 1024; /* 512k */
214 rb->strcpy(pInfo->name, "SST39VF040");
215 return true;
217 else
218 return false;
220 return false;
224 /*********** Tool Functions ************/
226 /* place a 32 bit value into memory, big endian */
227 void Write32(UINT8* pByte, UINT32 value)
229 pByte[0] = (UINT8)(value >> 24);
230 pByte[1] = (UINT8)(value >> 16);
231 pByte[2] = (UINT8)(value >> 8);
232 pByte[3] = (UINT8)(value);
235 /* read a 32 bit value from memory, big endian */
236 UINT32 Read32(UINT8* pByte)
238 UINT32 value;
240 value = (UINT32)pByte[0] << 24;
241 value |= (UINT32)pByte[1] << 16;
242 value |= (UINT32)pByte[2] << 8;
243 value |= (UINT32)pByte[3];
245 return value;
248 /* get the start address of the second image */
249 tImageHeader* GetSecondImage(void)
251 tImageHeader* pImage1;
252 UINT32 pos = 0; /* default: not found */
253 UINT32* pFlash = (UINT32*)FB;
255 /* determine the first image position */
256 pos = pFlash[2] + pFlash[3]; /* position + size of the bootloader
257 = after it */
258 pos = (pos + 3) & ~3; /* be sure it's 32 bit aligned */
259 pImage1 = (tImageHeader*)pos;
261 if (pImage1->destination != ROCKBOX_DEST ||
262 pImage1->execute != ROCKBOX_EXEC)
263 return 0; /* seems to be no Archos/Rockbox image in here */
265 if (pImage1->size != 0)
267 /* success, we have a second image */
268 pos = (UINT32)pImage1 + sizeof(tImageHeader) + pImage1->size;
269 if (((pos + SECTORSIZE-1) & ~(SECTORSIZE-1)) != pos)
270 { /* not sector-aligned */
271 pos = 0; /* sanity check failed */
275 return (tImageHeader*)pos;
279 /* Tool function to calculate a CRC32 across some buffer */
280 /* third argument is either 0xFFFFFFFF to start or value from last piece */
281 UINT32 crc_32(unsigned char* buf, unsigned len, unsigned crc32)
283 /* CCITT standard polynomial 0x04C11DB7 */
284 static const UINT32 crc32_lookup[16] =
285 { /* lookup table for 4 bits at a time is affordable */
286 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9,
287 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005,
288 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61,
289 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD
292 unsigned char byte;
293 UINT32 t;
295 while (len--)
297 byte = *buf++; /* get one byte of data */
299 /* upper nibble of our data */
300 t = crc32 >> 28; /* extract the 4 most significant bits */
301 t ^= byte >> 4; /* XOR in 4 bits of data into the extracted bits */
302 crc32 <<= 4; /* shift the CRC register left 4 bits */
303 crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */
305 /* lower nibble of our data */
306 t = crc32 >> 28; /* extract the 4 most significant bits */
307 t ^= byte & 0x0F; /* XOR in 4 bits of data into the extracted bits */
308 crc32 <<= 4; /* shift the CRC register left 4 bits */
309 crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */
312 return crc32;
316 /* test if the bootloader is up-to-date, returns 0 if yes, else CRC */
317 unsigned CheckBootloader(void)
319 unsigned crc;
320 UINT32* pFlash = (UINT32*)FB;
321 int bootloader_version = FB[BOOT_VERS_ADR];
323 if (bootloader_version) /* this is a newer image, with a version number */
325 if (bootloader_version < LATEST_BOOTLOADER_VERSION)
326 return bootloader_version;
327 else
328 return 0;
331 /* checksum the bootloader, unfortunately I have no version info yet */
332 crc = crc_32((unsigned char*)pFlash[2], pFlash[3], -1);
334 /* Here I have to check for ARCHOS_* defines in source code, which is
335 generally strongly discouraged. But here I'm not checking for a certain
336 feature, I'm checking for the model itself. */
337 #if defined(ARCHOS_PLAYER)
338 if (crc == 0x78DAC94A)
339 return 0;
340 #elif defined(ARCHOS_RECORDER)
341 if (crc == 0xE968702E || crc == 0x7C3D93B4) /* normal or ROMless each */
342 return 0;
343 #elif defined(ARCHOS_RECORDERV2)
344 if (crc == 0x4511E9B5 || crc == 0x3A93DBDF)
345 return 0;
346 #elif defined(ARCHOS_FMRECORDER)
347 if (crc == 0x4511E9B5 || crc == 0x3A93DBDF)
348 return 0;
349 #endif
351 return crc;
355 /*********** Image File Functions ************/
357 /* so far, only compressed images in UCL NRV algorithm 2e supported */
358 tCheckResult CheckImageFile(char* filename, int space, tImageHeader* pHeader,
359 UINT8* pos)
361 int i;
362 int fd;
363 int filesize; /* size info */
365 int fileread = 0; /* total size as read from the file */
366 int read; /* how many for this sector */
368 /* magic file header for compressed files */
369 static const UINT8 magic[8] = { 0x00,0xe9,0x55,0x43,0x4c,0xff,0x01,0x1a };
370 UINT8 ucl_header[UCL_HEADER];
372 fd = rb->open(filename, O_RDONLY);
373 if (fd < 0)
374 return eFileNotFound;
376 filesize = rb->filesize(fd);
377 if (filesize - (int)sizeof(ucl_header) - 8 > space)
379 rb->close(fd);
380 return eTooBig;
382 else if (filesize < 10000) /* give it some reasonable lower limit */
384 rb->close(fd);
385 return eTooSmall;
388 /* do some sanity checks */
390 read = rb->read(fd, ucl_header, sizeof(ucl_header));
391 fileread += read;
392 if (read != sizeof(ucl_header))
394 rb->close(fd);
395 return eReadErr;
398 /* compare the magic header */
399 for (i=0; i<8; i++)
401 if (ucl_header[i] != magic[i])
403 rb->close(fd);
404 return eNotUCL;
408 pHeader->size = Read32(ucl_header + 22); /* compressed size */
409 if (pHeader->size != filesize - sizeof(ucl_header) - 8)
411 rb->close(fd);
412 return eMultiBlocks;
415 /* fill in the hardcoded defaults of the header */
416 pHeader->destination = ROCKBOX_DEST;
417 pHeader->execute = ROCKBOX_EXEC;
419 if (Read32(ucl_header + 18) > pHeader->size) /* compare with uncompressed
420 size */
421 { /* compressed, normal case */
422 pHeader->flags = 0x00000001; /* flags for UCL compressed */
424 /* check for supported algorithm */
425 if (ucl_header[12] != 0x2E)
427 rb->close(fd);
428 return eWrongAlgorithm;
431 else
432 { /* uncompressed, either to be copied or run directly in flash */
433 UINT32 reset_vector; /* image has to start with reset vector */
435 pHeader->flags = 0x00000000; /* uncompressed */
437 read = rb->read(fd, &reset_vector, sizeof(reset_vector));
438 fileread += read;
439 if (read != sizeof(reset_vector))
441 rb->close(fd);
442 return eReadErr;
444 if (reset_vector >= (UINT32)FB
445 && reset_vector < (UINT32)FB+512*1024) /* ROM address? */
447 /* assume in-place, executing directly in flash */
448 pHeader->destination = (UINT32)(pos + sizeof(tImageHeader));
450 /* for new RomBox, this isn't the reset vector,
451 but the link address, for us to check the position */
452 if(pHeader->destination != reset_vector) /* compare link addr. */
454 rb->close(fd);
455 return eBadRomLink; /* not matching the start address */
458 /* read the now following reset vector */
459 read = rb->read(fd, &reset_vector, sizeof(reset_vector));
460 fileread += read;
461 if (read != sizeof(reset_vector))
463 rb->close(fd);
464 return eReadErr;
468 pHeader->execute = reset_vector;
471 /* check if we can read the whole file */
474 read = rb->read(fd, sector, SECTORSIZE);
475 fileread += read;
476 } while (read == SECTORSIZE);
478 rb->close(fd);
480 if (fileread != filesize)
481 return eReadErr;
483 return eOK;
487 /* returns the # of failures, 0 on success */
488 unsigned ProgramImageFile(char* filename, UINT8* pos,
489 tImageHeader* pImageHeader, int start, int size)
491 int i;
492 int fd;
493 int read; /* how many for this sector */
494 unsigned failures = 0;
496 fd = rb->open(filename, O_RDONLY);
497 if (fd < 0)
498 return false;
500 /* no error checking necessary here, we checked for minimum size
501 already */
502 rb->lseek(fd, start, SEEK_SET); /* go to start position */
504 *(tImageHeader*)sector = *pImageHeader; /* copy header into sector
505 buffer */
506 read = rb->read(fd, sector + sizeof(tImageHeader),
507 SECTORSIZE - sizeof(tImageHeader)); /* payload behind */
508 size -= read;
509 read += sizeof(tImageHeader); /* to be programmed, but not part of the
510 file */
512 do {
513 if (!EraseSector(pos))
515 /* nothing we can do, let the programming count the errors */
518 for (i=0; i<read; i++)
520 if (!ProgramByte(pos + i, sector[i]))
522 failures++;
526 pos += SECTORSIZE;
527 read = rb->read(fd, sector, (size > SECTORSIZE) ? SECTORSIZE : size);
528 /* payload for next sector */
529 size -= read;
530 } while (read > 0);
532 rb->close(fd);
534 return failures;
537 /* returns the # of failures, 0 on success */
538 unsigned VerifyImageFile(char* filename, UINT8* pos,
539 tImageHeader* pImageHeader, int start, int size)
541 int i;
542 int fd;
543 int read; /* how many for this sector */
544 unsigned failures = 0;
546 fd = rb->open(filename, O_RDONLY);
547 if (fd < 0)
548 return false;
550 /* no error checking necessary here, we checked for minimum size
551 already */
552 rb->lseek(fd, start, SEEK_SET); /* go to start position */
554 *(tImageHeader*)sector = *pImageHeader; /* copy header into sector
555 buffer */
556 read = rb->read(fd, sector + sizeof(tImageHeader),
557 SECTORSIZE - sizeof(tImageHeader)); /* payload behind */
559 size -= read;
560 read += sizeof(tImageHeader); /* to be programmed, but not part of the
561 file */
565 for (i=0; i<read; i++)
567 if (pos[i] != sector[i])
569 failures++;
573 pos += SECTORSIZE;
574 read = rb->read(fd, sector, (size > SECTORSIZE) ? SECTORSIZE : size);
575 /* payload for next sector */
576 size -= read;
577 } while (read);
579 rb->close(fd);
581 return failures;
585 /***************** User Interface Functions *****************/
587 int WaitForButton(void)
589 int button;
593 button = rb->button_get(true);
594 } while (button & BUTTON_REL);
596 return button;
599 /* helper for DoUserDialog() */
600 void ShowFlashInfo(tFlashInfo* pInfo, tImageHeader* pImageHeader)
602 char buf[32];
604 if (!pInfo->manufacturer)
606 rb->lcd_puts_scroll(0, 0, "Flash: M=?? D=??");
608 else
610 if (pInfo->size)
612 rb->snprintf(buf, sizeof(buf), "Flash size: %d KB",
613 pInfo->size / 1024);
614 rb->lcd_puts_scroll(0, 0, buf);
616 else
618 rb->lcd_puts_scroll(0, 0, "Unsupported chip");
623 if (pImageHeader)
625 rb->snprintf(buf, sizeof(buf), "Image at %d KB",
626 ((UINT8*)pImageHeader - FB) / 1024);
627 rb->lcd_puts_scroll(0, 1, buf);
629 else
631 rb->lcd_puts_scroll(0, 1, "No image found!");
636 /* Kind of our main function, defines the application flow. */
637 #ifdef HAVE_LCD_BITMAP
638 /* recorder version */
639 void DoUserDialog(char* filename)
641 tImageHeader ImageHeader;
642 tFlashInfo FlashInfo;
643 static char buf[MAX_PATH];
644 int button;
645 int rc; /* generic return code */
646 UINT32 space, aligned_size, true_size;
647 UINT8* pos;
648 ssize_t memleft;
649 UINT32 crc;
650 bool show_greet = false;
652 /* this can only work if Rockbox runs in DRAM, not flash ROM */
653 if ((UINT8*)rb >= FB && (UINT8*)rb < FB + 4096*1024) /* 4 MB max */
654 { /* we're running from flash */
655 rb->splash(HZ*3, "Not from ROM");
656 return; /* exit */
659 /* refuse to work if the power may fail meanwhile */
660 if (!rb->battery_level_safe())
662 rb->splash(HZ*3, "Battery too low!");
663 return; /* exit */
666 /* "allocate" memory */
667 sector = rb->plugin_get_buffer((size_t *)&memleft);
668 if (memleft < SECTORSIZE) /* need buffer for a flash sector */
670 rb->splash(HZ*3, "Out of memory");
671 return; /* exit */
674 rb->lcd_setfont(FONT_SYSFIXED);
676 pos = (void*)GetSecondImage();
677 rc = GetFlashInfo(&FlashInfo);
679 ShowFlashInfo(&FlashInfo, (void*)pos);
680 rb->lcd_update();
682 if (FlashInfo.size == 0) /* no valid chip */
684 rb->splash(HZ*3, "Not flashable");
685 return; /* exit */
687 else if (pos == 0)
689 rb->splash(HZ*3, "No image");
690 return; /* exit */
693 crc = CheckBootloader();
694 if (crc) /* outdated version found */
696 rb->snprintf(buf, sizeof(buf), "(check=0x%08lx)", crc);
697 rb->lcd_puts(0, 0, buf);
698 rb->lcd_puts(0, 1, "Hint: You're not ");
699 rb->lcd_puts(0, 2, "using the latest ");
700 rb->lcd_puts(0, 3, "bootloader. ");
701 rb->lcd_puts(0, 4, "A full reflash is ");
702 rb->lcd_puts(0, 5, "recommended, but ");
703 rb->lcd_puts(0, 6, "not required. ");
704 rb->lcd_puts(0, 7, "Press " KEYNAME1 " to ignore");
705 rb->lcd_update();
707 if (WaitForButton() != KEY1)
709 return;
711 rb->lcd_clear_display();
714 rb->lcd_puts(0, show_greet ? 0 : 3, "Checking...");
715 rb->lcd_update();
717 space = FlashInfo.size - (pos-FB + sizeof(ImageHeader));
718 /* size minus start */
720 rc = CheckImageFile(filename, space, &ImageHeader, pos);
721 if (rc != eOK)
723 rb->lcd_clear_display(); /* make room for error message */
724 show_greet = true; /* verbose */
727 rb->lcd_puts(0, show_greet ? 0 : 3, "Checked:");
728 switch (rc) {
729 case eOK:
730 rb->lcd_puts(0, show_greet ? 0 : 4, "File OK.");
731 break;
732 case eNotUCL:
733 rb->lcd_puts(0, 1, "File not UCL ");
734 rb->lcd_puts(0, 2, "compressed.");
735 rb->lcd_puts(0, 3, "Use uclpack --2e");
736 rb->lcd_puts(0, 4, " --10 rockbox.bin");
737 break;
738 case eWrongAlgorithm:
739 rb->lcd_puts(0, 1, "Wrong algorithm");
740 rb->lcd_puts(0, 2, "for compression.");
741 rb->lcd_puts(0, 3, "Use uclpack --2e");
742 rb->lcd_puts(0, 4, " --10 rockbox.bin");
743 break;
744 case eFileNotFound:
745 rb->lcd_puts(0, 1, "File not found:");
746 rb->lcd_puts_scroll(0, 2, filename);
747 break;
748 case eTooBig:
749 rb->lcd_puts(0, 1, "File too big,");
750 rb->lcd_puts(0, 2, "won't fit in chip.");
751 break;
752 case eTooSmall:
753 rb->lcd_puts(0, 1, "File too small.");
754 rb->lcd_puts(0, 2, "Incomplete?");
755 break;
756 case eReadErr:
757 rb->lcd_puts(0, 1, "File read error.");
758 break;
759 case eMultiBlocks:
760 rb->lcd_puts(0, 1, "File invalid.");
761 rb->lcd_puts(0, 2, "Blocksize");
762 rb->lcd_puts(0, 3, " too small?");
763 break;
764 case eBadRomLink:
765 rb->lcd_puts(0, 1, "RomBox mismatch.");
766 rb->lcd_puts(0, 2, "Wrong ROM position");
767 break;
768 default:
769 rb->lcd_puts(0, 1, "Check failed.");
770 break;
773 if (rc == eOK)
774 { /* was OK */
775 rb->lcd_puts(0, 6, "[" KEYNAME2 "] to program");
776 rb->lcd_puts(0, 7, "other key to exit");
778 else
779 { /* error occured */
780 rb->lcd_puts(0, 6, "Any key to exit");
782 rb->lcd_update();
784 button = WaitForButton();
785 if (rc != eOK || button != KEY2)
787 return;
790 true_size = ImageHeader.size;
791 aligned_size = ((sizeof(tImageHeader) + true_size + SECTORSIZE-1) &
792 ~(SECTORSIZE-1)) - sizeof(tImageHeader); /* round up to
793 next flash
794 sector */
795 ImageHeader.size = aligned_size; /* increase image size such that we reach
796 the next sector */
798 rb->lcd_clear_display();
799 rb->lcd_puts_scroll(0, 0, "Programming...");
800 rb->lcd_update();
802 rc = ProgramImageFile(filename, pos, &ImageHeader, UCL_HEADER, true_size);
803 if (rc)
804 { /* errors */
805 rb->lcd_clear_display();
806 rb->snprintf(buf, sizeof(buf), "%d errors", rc);
807 rb->lcd_puts(0, 0, "Error:");
808 rb->lcd_puts(0, 1, "Programming fail!");
809 rb->lcd_puts(0, 2, buf);
810 rb->lcd_update();
811 button = WaitForButton();
814 rb->lcd_clear_display();
815 rb->lcd_puts_scroll(0, 0, "Verifying...");
816 rb->lcd_update();
818 rc = VerifyImageFile(filename, pos, &ImageHeader, UCL_HEADER, true_size);
820 rb->lcd_clear_display();
821 if (rc == 0)
823 rb->lcd_puts(0, 0, "Verify OK.");
825 else
827 rb->snprintf(buf, sizeof(buf), "%d errors", rc);
828 rb->lcd_puts(0, 0, "Error:");
829 rb->lcd_puts(0, 1, "Verify fail!");
830 rb->lcd_puts(0, 2, buf);
831 rb->lcd_puts(0, 3, "Use safe image");
832 rb->lcd_puts(0, 4, "if booting hangs:");
833 rb->lcd_puts(0, 5, "F1 during power-on");
835 rb->lcd_puts(0, 7, "Any key to exit");
836 rb->lcd_update();
837 WaitForButton();
840 #else /* #ifdef HAVE_LCD_BITMAP */
842 /* Player version */
843 void DoUserDialog(char* filename)
845 tImageHeader ImageHeader;
846 tFlashInfo FlashInfo;
847 static char buf[MAX_PATH];
848 int button;
849 int rc; /* generic return code */
850 UINT32 space, aligned_size, true_size;
851 UINT8* pos;
852 ssize_t memleft;
853 UINT32 crc;
855 /* this can only work if Rockbox runs in DRAM, not flash ROM */
856 if ((UINT8*)rb >= FB && (UINT8*)rb < FB + 4096*1024) /* 4 MB max */
857 { /* we're running from flash */
858 rb->splash(HZ*3, "Not from ROM");
859 return; /* exit */
862 /* refuse to work if the power may fail meanwhile */
863 if (!rb->battery_level_safe())
865 rb->splash(HZ*3, "Batt. too low!");
866 return; /* exit */
869 /* "allocate" memory */
870 sector = rb->plugin_get_buffer((size_t *)&memleft);
871 if (memleft < SECTORSIZE) /* need buffer for a flash sector */
873 rb->splash(HZ*3, "Out of memory");
874 return; /* exit */
877 pos = (void*)GetSecondImage();
878 rc = GetFlashInfo(&FlashInfo);
880 if (FlashInfo.size == 0) /* no valid chip */
882 rb->splash(HZ*3, "Not flashable");
883 return; /* exit */
885 else if (pos == 0)
887 rb->splash(HZ*3, "No image");
888 return; /* exit */
891 crc = CheckBootloader();
892 if (crc) /* outdated version found */
894 rb->lcd_puts_scroll(0, 0, "Hint: You're not using the latest bootloader. A full reflash is recommended, but not required.");
895 rb->lcd_puts_scroll(0, 1, "Press [Menu] to ignore");
896 rb->lcd_update();
898 if (WaitForButton() != BUTTON_MENU)
900 return;
902 rb->lcd_clear_display();
905 rb->lcd_puts(0, 0, "Checking...");
906 rb->lcd_update();
908 space = FlashInfo.size - (pos-FB + sizeof(ImageHeader));
909 /* size minus start */
911 rc = CheckImageFile(filename, space, &ImageHeader, pos);
912 rb->lcd_puts(0, 0, "Checked:");
913 switch (rc) {
914 case eOK:
915 rb->lcd_puts(0, 1, "File OK.");
916 rb->sleep(HZ*1);
917 break;
918 case eNotUCL:
919 rb->lcd_puts_scroll(0, 1, "File not UCL compressed.");
920 break;
921 case eWrongAlgorithm:
922 rb->lcd_puts_scroll(0, 1, "Wrong compression algorithm.");
923 break;
924 case eFileNotFound:
925 rb->lcd_puts_scroll(0, 1, "File not found.");
926 break;
927 case eTooBig:
928 rb->lcd_puts_scroll(0, 1, "File too big.");
929 break;
930 case eTooSmall:
931 rb->lcd_puts_scroll(0, 1, "File too small. Incomplete?");
932 break;
933 case eReadErr:
934 rb->lcd_puts_scroll(0, 1, "File read error.");
935 break;
936 case eMultiBlocks:
937 rb->lcd_puts_scroll(0, 1, "File invalid. Blocksize too small?");
938 break;
939 case eBadRomLink:
940 rb->lcd_puts_scroll(0, 1, "BootBox mismatch");
941 break;
942 default:
943 rb->lcd_puts_scroll(0, 1, "Check failed.");
944 break;
946 rb->lcd_update();
948 if (rc == eOK)
949 { /* was OK */
950 rb->lcd_clear_display();
951 rb->lcd_puts_scroll(0, 0, "[ON] to program,");
952 rb->lcd_puts_scroll(0, 1, "other key to exit.");
954 else
955 { /* error occured */
956 WaitForButton();
957 rb->lcd_clear_display();
958 rb->lcd_puts_scroll(0, 0, "Flash failed.");
959 rb->lcd_puts_scroll(0, 1, "Any key to exit.");
961 rb->lcd_update();
963 button = WaitForButton();
964 if (rc != eOK || button != BUTTON_ON)
966 return;
969 true_size = ImageHeader.size;
970 aligned_size = ((sizeof(tImageHeader) + true_size + SECTORSIZE-1) &
971 ~(SECTORSIZE-1)) - sizeof(tImageHeader); /* round up to
972 next flash
973 sector */
974 ImageHeader.size = aligned_size; /* increase image size such that we reach
975 the next sector */
977 rb->lcd_clear_display();
978 rb->lcd_puts_scroll(0, 0, "Programming...");
979 rb->lcd_update();
981 rc = ProgramImageFile(filename, pos, &ImageHeader, UCL_HEADER, true_size);
982 if (rc)
983 { /* errors */
984 rb->lcd_clear_display();
985 rb->snprintf(buf, sizeof(buf), "%d errors", rc);
986 rb->lcd_puts_scroll(0, 0, "Programming failed!");
987 rb->lcd_puts_scroll(0, 1, buf);
988 rb->lcd_update();
989 button = WaitForButton();
992 rb->lcd_clear_display();
993 rb->lcd_puts_scroll(0, 0, "Verifying...");
994 rb->lcd_update();
996 rc = VerifyImageFile(filename, pos, &ImageHeader, UCL_HEADER, true_size);
998 rb->lcd_clear_display();
999 if (rc == 0)
1001 rb->lcd_puts(0, 0, "Verify OK.");
1002 rb->lcd_update();
1004 else
1006 rb->snprintf(buf, sizeof(buf), "Verify fail! %d errors", rc);
1007 rb->lcd_puts_scroll(0, 0, buf);
1008 rb->lcd_puts_scroll(0, 1, "Use safe image if booting hangs: [-] during power-on");
1009 rb->lcd_update();
1010 button = WaitForButton();
1014 #endif /* not HAVE_LCD_BITMAP */
1018 /***************** Plugin Entry Point *****************/
1020 enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter)
1022 int oldmode;
1024 rb = api; /* copy to global api pointer */
1026 if (parameter == NULL)
1028 rb->splash(HZ*3, "Play .ucl file!");
1029 return PLUGIN_OK;
1032 /* now go ahead and have fun! */
1033 oldmode = rb->system_memory_guard(MEMGUARD_NONE); /*disable memory guard */
1034 DoUserDialog((char*) parameter);
1035 rb->system_memory_guard(oldmode); /* re-enable memory guard */
1037 return PLUGIN_OK;
1041 #endif /* SH-target */