diff options
Diffstat (limited to 'apps/plugins/rockbox_flash.c')
-rw-r--r-- | apps/plugins/rockbox_flash.c | 967 |
1 files changed, 0 insertions, 967 deletions
diff --git a/apps/plugins/rockbox_flash.c b/apps/plugins/rockbox_flash.c deleted file mode 100644 index 4837b03fb5..0000000000 --- a/apps/plugins/rockbox_flash.c +++ /dev/null | |||
@@ -1,967 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
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 !!! | ||
12 | * | ||
13 | * Copyright (C) 2003 Jörg Hohensohn aka [IDC]Dragon | ||
14 | * | ||
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. | ||
19 | * | ||
20 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
21 | * KIND, either express or implied. | ||
22 | * | ||
23 | ****************************************************************************/ | ||
24 | #include "plugin.h" | ||
25 | |||
26 | /* define DUMMY if you only want to "play" with the UI, does no harm */ | ||
27 | /* #define DUMMY */ | ||
28 | |||
29 | #define LATEST_BOOTLOADER_VERSION 3 /* update this with the bootloader */ | ||
30 | |||
31 | #ifndef UINT8 | ||
32 | #define UINT8 unsigned char | ||
33 | #endif | ||
34 | |||
35 | #ifndef UINT16 | ||
36 | #define UINT16 unsigned short | ||
37 | #endif | ||
38 | |||
39 | #ifndef UINT32 | ||
40 | #define UINT32 unsigned long | ||
41 | #endif | ||
42 | |||
43 | /* hard-coded values */ | ||
44 | static volatile UINT8* FB = (UINT8*)0x02000000; /* Flash base address */ | ||
45 | #define SECTORSIZE 4096 /* size of one flash sector */ | ||
46 | |||
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 */ | ||
52 | |||
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 | ||
64 | |||
65 | typedef struct | ||
66 | { | ||
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; | ||
73 | |||
74 | /* result of the CheckFirmwareFile() function */ | ||
75 | typedef enum | ||
76 | { | ||
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; | ||
87 | |||
88 | typedef struct | ||
89 | { | ||
90 | UINT8 manufacturer; | ||
91 | UINT8 id; | ||
92 | int size; | ||
93 | char name[32]; | ||
94 | } tFlashInfo; | ||
95 | |||
96 | static UINT8* sector; /* better not place this on the stack... */ | ||
97 | |||
98 | /***************** Flash Functions *****************/ | ||
99 | |||
100 | |||
101 | /* read the manufacturer and device ID */ | ||
102 | static bool ReadID(volatile UINT8* pBase, UINT8* pManufacturerID, | ||
103 | UINT8* pDeviceID) | ||
104 | { | ||
105 | UINT8 not_manu, not_id; /* read values before switching to ID mode */ | ||
106 | UINT8 manu, id; /* read values when in ID mode */ | ||
107 | |||
108 | pBase = (UINT8*)((UINT32)pBase & 0xFFF80000); /* round down to 512k align, | ||
109 | to make sure */ | ||
110 | |||
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 */ | ||
114 | |||
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 */ | ||
119 | |||
120 | manu = pBase[0]; | ||
121 | id = pBase[1]; | ||
122 | |||
123 | pBase[0] = 0xF0; /* reset flash (back to normal read mode) */ | ||
124 | rb->sleep(HZ/50); /* Atmel wants 20ms pause here */ | ||
125 | |||
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 */ | ||
130 | { | ||
131 | *pManufacturerID = manu; /* return the results */ | ||
132 | *pDeviceID = id; | ||
133 | return true; /* success */ | ||
134 | } | ||
135 | return false; /* fail */ | ||
136 | } | ||
137 | |||
138 | /* erase the sector which contains the given address */ | ||
139 | static bool EraseSector(volatile UINT8* pAddr) | ||
140 | { | ||
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 */ | ||
149 | |||
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 */ | ||
156 | |||
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 */ | ||
160 | |||
161 | return (timeout != 0); | ||
162 | #endif | ||
163 | } | ||
164 | |||
165 | /* address must be in an erased location */ | ||
166 | static inline bool ProgramByte(volatile UINT8* pAddr, UINT8 data) | ||
167 | { | ||
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 */ | ||
174 | |||
175 | if (~*pAddr & data) /* just a safety feature, not really necessary */ | ||
176 | return false; /* can't set any bit from 0 to 1 */ | ||
177 | |||
178 | FB[0x5555] = 0xAA; /* enter command mode */ | ||
179 | FB[0x2AAA] = 0x55; | ||
180 | FB[0x5555] = 0xA0; /* byte program command */ | ||
181 | |||
182 | *pAddr = data; | ||
183 | |||
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 */ | ||
187 | |||
188 | return (timeout != 0); | ||
189 | #endif | ||
190 | } | ||
191 | |||
192 | /* this returns true if supported and fills the info struct */ | ||
193 | static bool GetFlashInfo(tFlashInfo* pInfo) | ||
194 | { | ||
195 | rb->memset(pInfo, 0, sizeof(tFlashInfo)); | ||
196 | |||
197 | if (!ReadID(FB, &pInfo->manufacturer, &pInfo->id)) | ||
198 | return false; | ||
199 | |||
200 | if (pInfo->manufacturer == 0xBF) /* SST */ | ||
201 | { | ||
202 | if (pInfo->id == 0xD6) | ||
203 | { | ||
204 | pInfo->size = 256* 1024; /* 256k */ | ||
205 | rb->strcpy(pInfo->name, "SST39VF020"); | ||
206 | return true; | ||
207 | } | ||
208 | else if (pInfo->id == 0xD7) | ||
209 | { | ||
210 | pInfo->size = 512* 1024; /* 512k */ | ||
211 | rb->strcpy(pInfo->name, "SST39VF040"); | ||
212 | return true; | ||
213 | } | ||
214 | else | ||
215 | return false; | ||
216 | } | ||
217 | return false; | ||
218 | } | ||
219 | |||
220 | |||
221 | /*********** Tool Functions ************/ | ||
222 | |||
223 | /* read a 32 bit value from memory, big endian */ | ||
224 | static UINT32 Read32(UINT8* pByte) | ||
225 | { | ||
226 | UINT32 value; | ||
227 | |||
228 | value = (UINT32)pByte[0] << 24; | ||
229 | value |= (UINT32)pByte[1] << 16; | ||
230 | value |= (UINT32)pByte[2] << 8; | ||
231 | value |= (UINT32)pByte[3]; | ||
232 | |||
233 | return value; | ||
234 | } | ||
235 | |||
236 | /* get the start address of the second image */ | ||
237 | static tImageHeader* GetSecondImage(void) | ||
238 | { | ||
239 | tImageHeader* pImage1; | ||
240 | UINT32 pos = 0; /* default: not found */ | ||
241 | UINT32* pFlash = (UINT32*)FB; | ||
242 | |||
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; | ||
248 | |||
249 | if (pImage1->destination != ROCKBOX_DEST || | ||
250 | pImage1->execute != ROCKBOX_EXEC) | ||
251 | return 0; /* seems to be no Archos/Rockbox image in here */ | ||
252 | |||
253 | if (pImage1->size != 0) | ||
254 | { | ||
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 */ | ||
260 | } | ||
261 | } | ||
262 | |||
263 | return (tImageHeader*)pos; | ||
264 | } | ||
265 | |||
266 | /* return bootloader version */ | ||
267 | static inline unsigned BootloaderVersion(void) | ||
268 | { | ||
269 | return FB[BOOT_VERS_ADR]; | ||
270 | } | ||
271 | |||
272 | /*********** Image File Functions ************/ | ||
273 | |||
274 | /* so far, only compressed images in UCL NRV algorithm 2e supported */ | ||
275 | tCheckResult CheckImageFile(char* filename, int space, | ||
276 | tImageHeader* pHeader, UINT8* pos) | ||
277 | { | ||
278 | int i; | ||
279 | int fd; | ||
280 | int filesize; /* size info */ | ||
281 | |||
282 | int fileread = 0; /* total size as read from the file */ | ||
283 | int read; /* how many for this sector */ | ||
284 | |||
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]; | ||
288 | |||
289 | fd = rb->open(filename, O_RDONLY); | ||
290 | if (fd < 0) | ||
291 | return eFileNotFound; | ||
292 | |||
293 | filesize = rb->filesize(fd); | ||
294 | if (filesize - (int)sizeof(ucl_header) - 8 > space) | ||
295 | { | ||
296 | rb->close(fd); | ||
297 | return eTooBig; | ||
298 | } | ||
299 | else if (filesize < 10000) /* give it some reasonable lower limit */ | ||
300 | { | ||
301 | rb->close(fd); | ||
302 | return eTooSmall; | ||
303 | } | ||
304 | |||
305 | /* do some sanity checks */ | ||
306 | |||
307 | read = rb->read(fd, ucl_header, sizeof(ucl_header)); | ||
308 | fileread += read; | ||
309 | if (read != sizeof(ucl_header)) | ||
310 | { | ||
311 | rb->close(fd); | ||
312 | return eReadErr; | ||
313 | } | ||
314 | |||
315 | /* compare the magic header */ | ||
316 | for (i=0; i<8; i++) | ||
317 | { | ||
318 | if (ucl_header[i] != magic[i]) | ||
319 | { | ||
320 | rb->close(fd); | ||
321 | return eNotUCL; | ||
322 | } | ||
323 | } | ||
324 | |||
325 | pHeader->size = Read32(ucl_header + 22); /* compressed size */ | ||
326 | if (pHeader->size != filesize - sizeof(ucl_header) - 8) | ||
327 | { | ||
328 | rb->close(fd); | ||
329 | return eMultiBlocks; | ||
330 | } | ||
331 | |||
332 | /* fill in the hardcoded defaults of the header */ | ||
333 | pHeader->destination = ROCKBOX_DEST; | ||
334 | pHeader->execute = ROCKBOX_EXEC; | ||
335 | |||
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 */ | ||
340 | |||
341 | /* check for supported algorithm */ | ||
342 | if (ucl_header[12] != 0x2E) | ||
343 | { | ||
344 | rb->close(fd); | ||
345 | return eWrongAlgorithm; | ||
346 | } | ||
347 | } | ||
348 | else | ||
349 | { /* uncompressed, either to be copied or run directly in flash */ | ||
350 | UINT32 reset_vector; /* image has to start with reset vector */ | ||
351 | |||
352 | pHeader->flags = 0x00000000; /* uncompressed */ | ||
353 | |||
354 | read = rb->read(fd, &reset_vector, sizeof(reset_vector)); | ||
355 | fileread += read; | ||
356 | if (read != sizeof(reset_vector)) | ||
357 | { | ||
358 | rb->close(fd); | ||
359 | return eReadErr; | ||
360 | } | ||
361 | if (reset_vector >= (UINT32)FB | ||
362 | && reset_vector < (UINT32)FB+512*1024) /* ROM address? */ | ||
363 | { | ||
364 | /* assume in-place, executing directly in flash */ | ||
365 | pHeader->destination = (UINT32)(pos + sizeof(tImageHeader)); | ||
366 | |||
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. */ | ||
370 | { | ||
371 | rb->close(fd); | ||
372 | return eBadRomLink; /* not matching the start address */ | ||
373 | } | ||
374 | |||
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)) | ||
379 | { | ||
380 | rb->close(fd); | ||
381 | return eReadErr; | ||
382 | } | ||
383 | } | ||
384 | |||
385 | pHeader->execute = reset_vector; | ||
386 | } | ||
387 | |||
388 | /* check if we can read the whole file */ | ||
389 | do | ||
390 | { | ||
391 | read = rb->read(fd, sector, SECTORSIZE); | ||
392 | fileread += read; | ||
393 | } while (read == SECTORSIZE); | ||
394 | |||
395 | rb->close(fd); | ||
396 | |||
397 | if (fileread != filesize) | ||
398 | return eReadErr; | ||
399 | |||
400 | return eOK; | ||
401 | } | ||
402 | |||
403 | |||
404 | /* returns the # of failures, 0 on success */ | ||
405 | static unsigned ProgramImageFile(char* filename, UINT8* pos, | ||
406 | tImageHeader* pImageHeader, | ||
407 | int start, int size) | ||
408 | { | ||
409 | int i; | ||
410 | int fd; | ||
411 | int read; /* how many for this sector */ | ||
412 | unsigned failures = 0; | ||
413 | |||
414 | fd = rb->open(filename, O_RDONLY); | ||
415 | if (fd < 0) | ||
416 | return false; | ||
417 | |||
418 | /* no error checking necessary here, we checked for minimum size | ||
419 | already */ | ||
420 | rb->lseek(fd, start, SEEK_SET); /* go to start position */ | ||
421 | |||
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 */ | ||
429 | |||
430 | do { | ||
431 | if (!EraseSector(pos)) | ||
432 | { | ||
433 | /* nothing we can do, let the programming count the errors */ | ||
434 | } | ||
435 | |||
436 | for (i=0; i<read; i++) | ||
437 | { | ||
438 | if (!ProgramByte(pos + i, sector[i])) | ||
439 | { | ||
440 | failures++; | ||
441 | } | ||
442 | } | ||
443 | |||
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); | ||
449 | |||
450 | rb->close(fd); | ||
451 | |||
452 | return failures; | ||
453 | } | ||
454 | |||
455 | /* returns the # of failures, 0 on success */ | ||
456 | static unsigned VerifyImageFile(char* filename, UINT8* pos, | ||
457 | tImageHeader* pImageHeader, | ||
458 | int start, int size) | ||
459 | { | ||
460 | int i; | ||
461 | int fd; | ||
462 | int read; /* how many for this sector */ | ||
463 | unsigned failures = 0; | ||
464 | |||
465 | fd = rb->open(filename, O_RDONLY); | ||
466 | if (fd < 0) | ||
467 | return false; | ||
468 | |||
469 | /* no error checking necessary here, we checked for minimum size | ||
470 | already */ | ||
471 | rb->lseek(fd, start, SEEK_SET); /* go to start position */ | ||
472 | |||
473 | *(tImageHeader*)sector = *pImageHeader; /* copy header into sector | ||
474 | buffer */ | ||
475 | read = rb->read(fd, sector + sizeof(tImageHeader), | ||
476 | SECTORSIZE - sizeof(tImageHeader)); /* payload behind */ | ||
477 | |||
478 | size -= read; | ||
479 | read += sizeof(tImageHeader); /* to be programmed, but not part of the | ||
480 | file */ | ||
481 | |||
482 | do | ||
483 | { | ||
484 | for (i=0; i<read; i++) | ||
485 | { | ||
486 | if (pos[i] != sector[i]) | ||
487 | { | ||
488 | failures++; | ||
489 | } | ||
490 | } | ||
491 | |||
492 | pos += SECTORSIZE; | ||
493 | read = rb->read(fd, sector, (size > SECTORSIZE) ? SECTORSIZE : size); | ||
494 | /* payload for next sector */ | ||
495 | size -= read; | ||
496 | } while (read); | ||
497 | |||
498 | rb->close(fd); | ||
499 | |||
500 | return failures; | ||
501 | } | ||
502 | |||
503 | |||
504 | /***************** User Interface Functions *****************/ | ||
505 | |||
506 | static int WaitForButton(void) | ||
507 | { | ||
508 | int button; | ||
509 | |||
510 | do | ||
511 | { | ||
512 | button = rb->button_get(true); | ||
513 | } while (IS_SYSEVENT(button) || (button & BUTTON_REL)); | ||
514 | |||
515 | return button; | ||
516 | } | ||
517 | |||
518 | #ifdef HAVE_LCD_BITMAP | ||
519 | /* helper for DoUserDialog() */ | ||
520 | static void ShowFlashInfo(tFlashInfo* pInfo, tImageHeader* pImageHeader) | ||
521 | { | ||
522 | char buf[32]; | ||
523 | |||
524 | if (!pInfo->manufacturer) | ||
525 | { | ||
526 | rb->lcd_puts_scroll(0, 0, "Flash: M=?? D=??"); | ||
527 | } | ||
528 | else | ||
529 | { | ||
530 | if (pInfo->size) | ||
531 | { | ||
532 | rb->snprintf(buf, sizeof(buf), "Flash size: %d KB", | ||
533 | pInfo->size / 1024); | ||
534 | rb->lcd_puts_scroll(0, 0, buf); | ||
535 | } | ||
536 | else | ||
537 | { | ||
538 | rb->lcd_puts_scroll(0, 0, "Unsupported chip"); | ||
539 | } | ||
540 | |||
541 | } | ||
542 | |||
543 | if (pImageHeader) | ||
544 | { | ||
545 | rb->snprintf(buf, sizeof(buf), "Image at %d KB", | ||
546 | ((UINT8*)pImageHeader - FB) / 1024); | ||
547 | rb->lcd_puts_scroll(0, 1, buf); | ||
548 | } | ||
549 | else | ||
550 | { | ||
551 | rb->lcd_puts_scroll(0, 1, "No image found!"); | ||
552 | } | ||
553 | } | ||
554 | |||
555 | |||
556 | /* Kind of our main function, defines the application flow. */ | ||
557 | /* recorder version */ | ||
558 | static void DoUserDialog(char* filename) | ||
559 | { | ||
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; | ||
569 | |||
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 */ | ||
575 | } | ||
576 | |||
577 | /* refuse to work if the power may fail meanwhile */ | ||
578 | if (!rb->battery_level_safe()) | ||
579 | { | ||
580 | rb->splash(HZ*3, "Battery too low!"); | ||
581 | return; /* exit */ | ||
582 | } | ||
583 | |||
584 | /* "allocate" memory */ | ||
585 | sector = rb->plugin_get_buffer(&memleft); | ||
586 | if (memleft < SECTORSIZE) /* need buffer for a flash sector */ | ||
587 | { | ||
588 | rb->splash(HZ*3, "Out of memory"); | ||
589 | return; /* exit */ | ||
590 | } | ||
591 | |||
592 | rb->lcd_setfont(FONT_SYSFIXED); | ||
593 | |||
594 | pos = (void*)GetSecondImage(); | ||
595 | rc = GetFlashInfo(&FlashInfo); | ||
596 | |||
597 | ShowFlashInfo(&FlashInfo, (void*)pos); | ||
598 | rb->lcd_update(); | ||
599 | |||
600 | if (FlashInfo.size == 0) /* no valid chip */ | ||
601 | { | ||
602 | rb->splash(HZ*3, "Not flashable"); | ||
603 | return; /* exit */ | ||
604 | } | ||
605 | else if (pos == 0) | ||
606 | { | ||
607 | rb->splash(HZ*3, "No image"); | ||
608 | return; /* exit */ | ||
609 | } | ||
610 | |||
611 | bl_version = BootloaderVersion(); | ||
612 | /* Upgrade currently not recommended for FM and V2 | ||
613 | recorder due to bugs in V3 BootBox. (FS#12426) */ | ||
614 | #if !defined(ARCHOS_FMRECORDER) && !defined(ARCHOS_RECORDERV2) | ||
615 | if (bl_version < LATEST_BOOTLOADER_VERSION) | ||
616 | { | ||
617 | rb->lcd_putsf(0, 0, "Bootloader V%d", bl_version); | ||
618 | rb->lcd_puts(0, 1, "Hint: You're not "); | ||
619 | rb->lcd_puts(0, 2, "using the latest "); | ||
620 | rb->lcd_puts(0, 3, "bootloader. "); | ||
621 | rb->lcd_puts(0, 4, "A full reflash is "); | ||
622 | rb->lcd_puts(0, 5, "recommended. "); | ||
623 | rb->lcd_puts(0, 6, "Press " KEYNAME1 " to ignore"); | ||
624 | rb->lcd_update(); | ||
625 | |||
626 | if (WaitForButton() != KEY1) | ||
627 | { | ||
628 | return; | ||
629 | } | ||
630 | rb->lcd_clear_display(); | ||
631 | } | ||
632 | #endif | ||
633 | |||
634 | rb->lcd_puts(0, show_greet ? 0 : 3, "Checking..."); | ||
635 | rb->lcd_update(); | ||
636 | |||
637 | space = FlashInfo.size - (pos-FB + sizeof(ImageHeader)); | ||
638 | /* size minus start */ | ||
639 | |||
640 | rc = CheckImageFile(filename, space, &ImageHeader, pos); | ||
641 | if (rc != eOK) | ||
642 | { | ||
643 | rb->lcd_clear_display(); /* make room for error message */ | ||
644 | show_greet = true; /* verbose */ | ||
645 | } | ||
646 | |||
647 | rb->lcd_puts(0, show_greet ? 0 : 3, "Checked:"); | ||
648 | switch (rc) { | ||
649 | case eOK: | ||
650 | rb->lcd_puts(0, show_greet ? 0 : 4, "File OK."); | ||
651 | break; | ||
652 | case eNotUCL: | ||
653 | rb->lcd_puts(0, 1, "File not UCL "); | ||
654 | rb->lcd_puts(0, 2, "compressed."); | ||
655 | rb->lcd_puts(0, 3, "Use uclpack --2e"); | ||
656 | rb->lcd_puts(0, 4, " --10 rockbox.bin"); | ||
657 | break; | ||
658 | case eWrongAlgorithm: | ||
659 | rb->lcd_puts(0, 1, "Wrong algorithm"); | ||
660 | rb->lcd_puts(0, 2, "for compression."); | ||
661 | rb->lcd_puts(0, 3, "Use uclpack --2e"); | ||
662 | rb->lcd_puts(0, 4, " --10 rockbox.bin"); | ||
663 | break; | ||
664 | case eFileNotFound: | ||
665 | rb->lcd_puts(0, 1, "File not found:"); | ||
666 | rb->lcd_puts_scroll(0, 2, filename); | ||
667 | break; | ||
668 | case eTooBig: | ||
669 | rb->lcd_puts(0, 1, "File too big,"); | ||
670 | rb->lcd_puts(0, 2, "won't fit in chip."); | ||
671 | if (bl_version < LATEST_BOOTLOADER_VERSION) | ||
672 | { | ||
673 | rb->lcd_puts(0, 3, "Upgrade bootloader"); | ||
674 | } | ||
675 | break; | ||
676 | case eTooSmall: | ||
677 | rb->lcd_puts(0, 1, "File too small."); | ||
678 | rb->lcd_puts(0, 2, "Incomplete?"); | ||
679 | break; | ||
680 | case eReadErr: | ||
681 | rb->lcd_puts(0, 1, "File read error."); | ||
682 | break; | ||
683 | case eMultiBlocks: | ||
684 | rb->lcd_puts(0, 1, "File invalid."); | ||
685 | rb->lcd_puts(0, 2, "Blocksize"); | ||
686 | rb->lcd_puts(0, 3, " too small?"); | ||
687 | break; | ||
688 | case eBadRomLink: | ||
689 | rb->lcd_puts(0, 1, "Bootloader not"); | ||
690 | rb->lcd_puts(0, 2, "compatible with"); | ||
691 | rb->lcd_puts(0, 3, "RomBox. Start"); | ||
692 | rb->lcd_puts(0, 4, "address mismatch."); | ||
693 | break; | ||
694 | default: | ||
695 | rb->lcd_puts(0, 1, "Check failed."); | ||
696 | break; | ||
697 | } | ||
698 | |||
699 | if (rc == eOK) | ||
700 | { /* was OK */ | ||
701 | rb->lcd_puts(0, 6, "[" KEYNAME2 "] to program"); | ||
702 | rb->lcd_puts(0, 7, "other key to exit"); | ||
703 | } | ||
704 | else | ||
705 | { /* error occured */ | ||
706 | rb->lcd_puts(0, 6, "Any key to exit"); | ||
707 | } | ||
708 | rb->lcd_update(); | ||
709 | |||
710 | button = WaitForButton(); | ||
711 | if (rc != eOK || button != KEY2) | ||
712 | { | ||
713 | return; | ||
714 | } | ||
715 | |||
716 | true_size = ImageHeader.size; | ||
717 | aligned_size = ((sizeof(tImageHeader) + true_size + SECTORSIZE-1) & | ||
718 | ~(SECTORSIZE-1)) - sizeof(tImageHeader); /* round up to | ||
719 | next flash | ||
720 | sector */ | ||
721 | ImageHeader.size = aligned_size; /* increase image size such that we reach | ||
722 | the next sector */ | ||
723 | |||
724 | rb->lcd_clear_display(); | ||
725 | rb->lcd_puts_scroll(0, 0, "Programming..."); | ||
726 | rb->lcd_update(); | ||
727 | |||
728 | rc = ProgramImageFile(filename, pos, &ImageHeader, UCL_HEADER, true_size); | ||
729 | if (rc) | ||
730 | { /* errors */ | ||
731 | rb->lcd_clear_display(); | ||
732 | rb->lcd_puts(0, 0, "Error:"); | ||
733 | rb->lcd_puts(0, 1, "Programming fail!"); | ||
734 | rb->lcd_putsf(0, 2, "%d errors", rc); | ||
735 | rb->lcd_update(); | ||
736 | button = WaitForButton(); | ||
737 | } | ||
738 | |||
739 | rb->lcd_clear_display(); | ||
740 | rb->lcd_puts_scroll(0, 0, "Verifying..."); | ||
741 | rb->lcd_update(); | ||
742 | |||
743 | rc = VerifyImageFile(filename, pos, &ImageHeader, UCL_HEADER, true_size); | ||
744 | |||
745 | rb->lcd_clear_display(); | ||
746 | if (rc == 0) | ||
747 | { | ||
748 | rb->lcd_puts(0, 0, "Verify OK."); | ||
749 | } | ||
750 | else | ||
751 | { | ||
752 | rb->lcd_puts(0, 0, "Error:"); | ||
753 | rb->lcd_puts(0, 1, "Verify fail!"); | ||
754 | rb->lcd_putsf(0, 2, "%d errors", rc); | ||
755 | rb->lcd_puts(0, 3, "Use safe image"); | ||
756 | rb->lcd_puts(0, 4, "if booting hangs:"); | ||
757 | rb->lcd_puts(0, 5, "F1 during power-on"); | ||
758 | } | ||
759 | rb->lcd_puts(0, 7, "Any key to exit"); | ||
760 | rb->lcd_update(); | ||
761 | WaitForButton(); | ||
762 | } | ||
763 | |||
764 | #else /* #ifdef HAVE_LCD_BITMAP */ | ||
765 | |||
766 | /* Player version */ | ||
767 | static void DoUserDialog(char* filename) | ||
768 | { | ||
769 | tImageHeader ImageHeader; | ||
770 | tFlashInfo FlashInfo; | ||
771 | static char buf[MAX_PATH]; | ||
772 | int button; | ||
773 | int rc; /* generic return code */ | ||
774 | UINT32 space, aligned_size, true_size; | ||
775 | UINT8* pos; | ||
776 | size_t memleft; | ||
777 | unsigned bl_version; | ||
778 | |||
779 | /* this can only work if Rockbox runs in DRAM, not flash ROM */ | ||
780 | if ((UINT8*)rb >= FB && (UINT8*)rb < FB + 4096*1024) /* 4 MB max */ | ||
781 | { /* we're running from flash */ | ||
782 | rb->splash(HZ*3, "Not from ROM"); | ||
783 | return; /* exit */ | ||
784 | } | ||
785 | |||
786 | /* refuse to work if the power may fail meanwhile */ | ||
787 | if (!rb->battery_level_safe()) | ||
788 | { | ||
789 | rb->splash(HZ*3, "Batt. too low!"); | ||
790 | return; /* exit */ | ||
791 | } | ||
792 | |||
793 | /* "allocate" memory */ | ||
794 | sector = rb->plugin_get_buffer(&memleft); | ||
795 | if (memleft < SECTORSIZE) /* need buffer for a flash sector */ | ||
796 | { | ||
797 | rb->splash(HZ*3, "Out of memory"); | ||
798 | return; /* exit */ | ||
799 | } | ||
800 | |||
801 | pos = (void*)GetSecondImage(); | ||
802 | rc = GetFlashInfo(&FlashInfo); | ||
803 | |||
804 | if (FlashInfo.size == 0) /* no valid chip */ | ||
805 | { | ||
806 | rb->splash(HZ*3, "Not flashable"); | ||
807 | return; /* exit */ | ||
808 | } | ||
809 | else if (pos == 0) | ||
810 | { | ||
811 | rb->splash(HZ*3, "No image"); | ||
812 | return; /* exit */ | ||
813 | } | ||
814 | |||
815 | bl_version = BootloaderVersion(); | ||
816 | if (bl_version < LATEST_BOOTLOADER_VERSION) | ||
817 | { | ||
818 | rb->lcd_puts_scroll(0, 0, "Hint: You're not using the latest bootloader. A full reflash is recommended, but not required."); | ||
819 | rb->lcd_puts_scroll(0, 1, "Press [Menu] to ignore"); | ||
820 | rb->lcd_update(); | ||
821 | |||
822 | if (WaitForButton() != BUTTON_MENU) | ||
823 | { | ||
824 | return; | ||
825 | } | ||
826 | rb->lcd_clear_display(); | ||
827 | } | ||
828 | |||
829 | rb->lcd_puts(0, 0, "Checking..."); | ||
830 | rb->lcd_update(); | ||
831 | |||
832 | space = FlashInfo.size - (pos-FB + sizeof(ImageHeader)); | ||
833 | /* size minus start */ | ||
834 | |||
835 | rc = CheckImageFile(filename, space, &ImageHeader, pos); | ||
836 | rb->lcd_puts(0, 0, "Checked:"); | ||
837 | switch (rc) { | ||
838 | case eOK: | ||
839 | rb->lcd_puts(0, 1, "File OK."); | ||
840 | rb->sleep(HZ*1); | ||
841 | break; | ||
842 | case eNotUCL: | ||
843 | rb->lcd_puts_scroll(0, 1, "File not UCL compressed."); | ||
844 | break; | ||
845 | case eWrongAlgorithm: | ||
846 | rb->lcd_puts_scroll(0, 1, "Wrong compression algorithm."); | ||
847 | break; | ||
848 | case eFileNotFound: | ||
849 | rb->lcd_puts_scroll(0, 1, "File not found."); | ||
850 | break; | ||
851 | case eTooBig: | ||
852 | if (bl_version < LATEST_BOOTLOADER_VERSION) | ||
853 | { | ||
854 | rb->lcd_puts_scroll(0, 1, "File too big, upgrade bootloader."); | ||
855 | } | ||
856 | else | ||
857 | { | ||
858 | rb->lcd_puts_scroll(0, 1, "File too big."); | ||
859 | } | ||
860 | break; | ||
861 | case eTooSmall: | ||
862 | rb->lcd_puts_scroll(0, 1, "File too small. Incomplete?"); | ||
863 | break; | ||
864 | case eReadErr: | ||
865 | rb->lcd_puts_scroll(0, 1, "File read error."); | ||
866 | break; | ||
867 | case eMultiBlocks: | ||
868 | rb->lcd_puts_scroll(0, 1, "File invalid. Blocksize too small?"); | ||
869 | break; | ||
870 | case eBadRomLink: | ||
871 | rb->lcd_puts_scroll(0, 1, "Bootloader not compatible with RomBox."); | ||
872 | break; | ||
873 | default: | ||
874 | rb->lcd_puts_scroll(0, 1, "Check failed."); | ||
875 | break; | ||
876 | } | ||
877 | rb->lcd_update(); | ||
878 | |||
879 | if (rc == eOK) | ||
880 | { /* was OK */ | ||
881 | rb->lcd_clear_display(); | ||
882 | rb->lcd_puts_scroll(0, 0, "[ON] to program,"); | ||
883 | rb->lcd_puts_scroll(0, 1, "other key to exit."); | ||
884 | } | ||
885 | else | ||
886 | { /* error occured */ | ||
887 | WaitForButton(); | ||
888 | rb->lcd_clear_display(); | ||
889 | rb->lcd_puts_scroll(0, 0, "Flash failed."); | ||
890 | rb->lcd_puts_scroll(0, 1, "Any key to exit."); | ||
891 | } | ||
892 | rb->lcd_update(); | ||
893 | |||
894 | button = WaitForButton(); | ||
895 | if (rc != eOK || button != BUTTON_ON) | ||
896 | { | ||
897 | return; | ||
898 | } | ||
899 | |||
900 | true_size = ImageHeader.size; | ||
901 | aligned_size = ((sizeof(tImageHeader) + true_size + SECTORSIZE-1) & | ||
902 | ~(SECTORSIZE-1)) - sizeof(tImageHeader); /* round up to | ||
903 | next flash | ||
904 | sector */ | ||
905 | ImageHeader.size = aligned_size; /* increase image size such that we reach | ||
906 | the next sector */ | ||
907 | |||
908 | rb->lcd_clear_display(); | ||
909 | rb->lcd_puts_scroll(0, 0, "Programming..."); | ||
910 | rb->lcd_update(); | ||
911 | |||
912 | rc = ProgramImageFile(filename, pos, &ImageHeader, UCL_HEADER, true_size); | ||
913 | if (rc) | ||
914 | { /* errors */ | ||
915 | rb->lcd_clear_display(); | ||
916 | rb->snprintf(buf, sizeof(buf), "%d errors", rc); | ||
917 | rb->lcd_puts_scroll(0, 0, "Programming failed!"); | ||
918 | rb->lcd_puts_scroll(0, 1, buf); | ||
919 | rb->lcd_update(); | ||
920 | button = WaitForButton(); | ||
921 | } | ||
922 | |||
923 | rb->lcd_clear_display(); | ||
924 | rb->lcd_puts_scroll(0, 0, "Verifying..."); | ||
925 | rb->lcd_update(); | ||
926 | |||
927 | rc = VerifyImageFile(filename, pos, &ImageHeader, UCL_HEADER, true_size); | ||
928 | |||
929 | rb->lcd_clear_display(); | ||
930 | if (rc == 0) | ||
931 | { | ||
932 | rb->lcd_puts(0, 0, "Verify OK."); | ||
933 | rb->lcd_update(); | ||
934 | } | ||
935 | else | ||
936 | { | ||
937 | rb->snprintf(buf, sizeof(buf), "Verify fail! %d errors", rc); | ||
938 | rb->lcd_puts_scroll(0, 0, buf); | ||
939 | rb->lcd_puts_scroll(0, 1, "Use safe image if booting hangs: [-] during power-on"); | ||
940 | rb->lcd_update(); | ||
941 | button = WaitForButton(); | ||
942 | } | ||
943 | } | ||
944 | |||
945 | #endif /* not HAVE_LCD_BITMAP */ | ||
946 | |||
947 | |||
948 | |||
949 | /***************** Plugin Entry Point *****************/ | ||
950 | |||
951 | enum plugin_status plugin_start(const void* parameter) | ||
952 | { | ||
953 | int oldmode; | ||
954 | |||
955 | if (parameter == NULL) | ||
956 | { | ||
957 | rb->splash(HZ*3, "Play .ucl file!"); | ||
958 | return PLUGIN_OK; | ||
959 | } | ||
960 | |||
961 | /* now go ahead and have fun! */ | ||
962 | oldmode = rb->system_memory_guard(MEMGUARD_NONE); /*disable memory guard */ | ||
963 | DoUserDialog((char*) parameter); | ||
964 | rb->system_memory_guard(oldmode); /* re-enable memory guard */ | ||
965 | |||
966 | return PLUGIN_OK; | ||
967 | } | ||