summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/plugins/rockbox_flash.c664
1 files changed, 664 insertions, 0 deletions
diff --git a/apps/plugins/rockbox_flash.c b/apps/plugins/rockbox_flash.c
new file mode 100644
index 0000000000..829cc5a346
--- /dev/null
+++ b/apps/plugins/rockbox_flash.c
@@ -0,0 +1,664 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id$
9*
10* Plugin for reprogramming only the second Rockbox image.
11*
12* Copyright (C) 2003 Jörg Hohensohn [IDC]Dragon
13*
14* All files in this archive are subject to the GNU General Public License.
15* See the file COPYING in the source tree root for full license agreement.
16*
17* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18* KIND, either express or implied.
19*
20****************************************************************************/
21#include "plugin.h"
22
23#ifndef UINT8
24#define UINT8 unsigned char
25#endif
26
27#ifndef UINT16
28#define UINT16 unsigned short
29#endif
30
31#ifndef UINT32
32#define UINT32 unsigned long
33#endif
34
35// hard-coded values
36static volatile UINT8* FB = (UINT8*)0x02000000; // Flash base address
37#define SECTORSIZE 4096 // size of one flash sector
38
39#define ROCKBOX_DEST 0x09000000
40#define ROCKBOX_EXEC 0x09000200
41#define FILENAME "/rockbox.ucl"
42#define VERS_ADR 0xFE // position of firmware version value in Flash
43#define UCL_HEADER 26 // size of the header generated by uclpack
44
45typedef struct
46{
47 UINT32 destination; // address to copy it to
48 UINT32 size; // how many bytes of payload (to the next header)
49 UINT32 execute; // entry point
50 UINT32 flags; // uncompressed or compressed
51 // end of header, now comes the payload
52} tImageHeader;
53
54
55// result of the CheckFirmwareFile() function
56typedef enum
57{
58 eOK = 0,
59 eFileNotFound, // errors from here on
60 eTooBig,
61 eTooSmall,
62 eReadErr,
63 eNotUCL,
64 eWrongAlgorithm,
65 eMultiBlocks,
66} tCheckResult;
67
68typedef struct
69{
70 UINT8 manufacturer;
71 UINT8 id;
72 int size;
73 char name[32];
74} tFlashInfo;
75
76static struct plugin_api* rb; // here is a global api struct pointer
77
78#define SEC_SIZE 4096 // size of one flash sector
79static UINT8 sector[SEC_SIZE]; // better not place this on the stack...
80
81
82/***************** Flash Functions *****************/
83
84
85// read the manufacturer and device ID
86bool ReadID(volatile UINT8* pBase, UINT8* pManufacturerID, UINT8* pDeviceID)
87{
88 UINT8 not_manu, not_id; // read values before switching to ID mode
89 UINT8 manu, id; // read values when in ID mode
90
91 pBase = (UINT8*)((UINT32)pBase & 0xFFF80000); // round down to 512k align, to make shure
92
93 not_manu = pBase[0]; // read the normal content
94 not_id = pBase[1]; // should be 'A' (0x41) and 'R' (0x52) from the "ARCH" marker
95
96 pBase[0x5555] = 0xAA; // enter command mode
97 pBase[0x2AAA] = 0x55;
98 pBase[0x5555] = 0x90; // ID command
99 rb->sleep(HZ/50); // Atmel wants 20ms pause here
100
101 manu = pBase[0];
102 id = pBase[1];
103
104 pBase[0] = 0xF0; // reset flash (back to normal read mode)
105 rb->sleep(HZ/50); // Atmel wants 20ms pause here
106
107 /* I assume success if the obtained values are different from
108 the normal flash content. This is not perfectly bulletproof, they
109 could theoretically be the same by chance, causing us to fail. */
110 if (not_manu != manu || not_id != id) // a value has changed
111 {
112 *pManufacturerID = manu; // return the results
113 *pDeviceID = id;
114 return true; // success
115 }
116 return false; // fail
117}
118
119
120// eraze the sector which contains the given address
121bool ErazeSector(volatile UINT8* pAddr)
122{
123 volatile UINT8* pBase = (UINT8*)((UINT32)pAddr & 0xFFF80000); // round down to 512k align
124 unsigned timeout = 43000; // the timeout loop should be no less than 25ms
125
126 pBase[0x5555] = 0xAA; // enter command mode
127 pBase[0x2AAA] = 0x55;
128 pBase[0x5555] = 0x80; // eraze command
129 pBase[0x5555] = 0xAA; // enter command mode
130 pBase[0x2AAA] = 0x55;
131 *pAddr = 0x30; // eraze the sector
132
133 // I counted 7 instructions for this loop -> min. 0.58 us per round
134 // Plus memory waitstates it will be much more, gives margin
135 while (*pAddr != 0xFF && --timeout); // poll for erazed
136
137 return (timeout != 0);
138}
139
140// address must be in an erazed location
141inline bool ProgramByte(volatile UINT8* pAddr, UINT8 data)
142{
143 unsigned timeout = 35; // the timeout loop should be no less than 20us
144
145 if (~*pAddr & data) // just a safety feature, not really necessary
146 return false; // can't set any bit from 0 to 1
147
148 FB[0x5555] = 0xAA; // enter command mode
149 FB[0x2AAA] = 0x55;
150 FB[0x5555] = 0xA0; // byte program command
151
152 *pAddr = data;
153
154 // I counted 7 instructions for this loop -> min. 0.58 us per round
155 // Plus memory waitstates it will be much more, gives margin
156 while (*pAddr != data && --timeout); // poll for programmed
157
158 return (timeout != 0);
159}
160
161// this returns true if supported and fills the info struct
162bool GetFlashInfo(tFlashInfo* pInfo)
163{
164 rb->memset(pInfo, 0, sizeof(tFlashInfo));
165
166 if (!ReadID(FB, &pInfo->manufacturer, &pInfo->id))
167 return false;
168
169 if (pInfo->manufacturer == 0xBF) // SST
170 {
171 if (pInfo->id == 0xD6)
172 {
173 pInfo->size = 256* 1024; // 256k
174 rb->strcpy(pInfo->name, "SST39VF020");
175 return true;
176 }
177 else if (pInfo->id == 0xD7)
178 {
179 pInfo->size = 512* 1024; // 512k
180 rb->strcpy(pInfo->name, "SST39VF040");
181 return true;
182 }
183 else
184 return false;
185 }
186 return false;
187}
188
189
190/*********** Tool Functions ************/
191
192// place a 32 bit value into memory, big endian
193void Write32(UINT8* pByte, UINT32 value)
194{
195 pByte[0] = (UINT8)(value >> 24);
196 pByte[1] = (UINT8)(value >> 16);
197 pByte[2] = (UINT8)(value >> 8);
198 pByte[3] = (UINT8)(value);
199}
200
201// read a 32 bit value from memory, big endian
202UINT32 Read32(UINT8* pByte)
203{
204 UINT32 value = 0;
205
206 value |= (UINT32)pByte[0] << 24;
207 value |= (UINT32)pByte[1] << 16;
208 value |= (UINT32)pByte[2] << 8;
209 value |= (UINT32)pByte[3];
210
211 return value;
212}
213
214
215// get the start address of the second image
216tImageHeader* GetSecondImage(void)
217{
218 tImageHeader* pImage1;
219 UINT32 pos = 0; // dafault: not found
220 UINT32* pFlash = (UINT32*)FB;
221
222 UINT16 version = *(UINT16*)(FB + VERS_ADR);
223 if (version < 200) // at least 2.00
224 {
225 return 0; // not our flash layout
226 }
227
228 // determine the first image position
229 pos = pFlash[2] + pFlash[3]; // position + size of the bootloader = after it
230 pos = (pos + 3) & ~3; // be shure it's 32 bit aligned
231 pImage1 = (tImageHeader*)pos;
232
233 if (pImage1->destination != ROCKBOX_DEST || pImage1->execute != ROCKBOX_EXEC)
234 return 0; // seems to be no Rockbox stuff in here
235
236 if (pImage1->size != 0)
237 { // success, we have a second image
238 pos = (UINT32)pImage1 + sizeof(tImageHeader) + pImage1->size;
239 if (((pos + SECTORSIZE-1) & ~(SECTORSIZE-1)) != pos)
240 { // not sector-aligned
241 pos = 0; // sanity check failed
242 }
243 }
244
245 return (tImageHeader*)pos;
246}
247
248
249/*********** Image File Functions ************/
250
251// so far, only compressed images in UCL NRV algorithm 2e supported
252tCheckResult CheckImageFile(char* filename, int space, tImageHeader* pHeader)
253{
254 int i;
255 int fd;
256 int filesize; // size info
257
258 int fileread = 0; // total size as read from the file
259 int read; // how many for this sector
260
261 // magic file header for compressed files
262 static const UINT8 magic[8] = { 0x00,0xe9,0x55,0x43,0x4c,0xff,0x01,0x1a };
263 UINT8 ucl_header[UCL_HEADER];
264
265 fd = rb->open(filename, O_RDONLY);
266 if (fd < 0)
267 return eFileNotFound;
268
269 filesize = rb->filesize(fd);
270 if (filesize - (int)sizeof(ucl_header) - 8 > space)
271 {
272 rb->close(fd);
273 return eTooBig;
274 }
275 else if (filesize < 50000) // give it some reasonable lower limit
276 {
277 rb->close(fd);
278 return eTooSmall;
279 }
280
281 // do some sanity checks
282
283 read = rb->read(fd, ucl_header, sizeof(ucl_header));
284 fileread += read;
285 if (read != sizeof(ucl_header))
286 {
287 rb->close(fd);
288 return eReadErr;
289 }
290
291 // compare the magic header
292 for (i=0; i<8; i++)
293 {
294 if (ucl_header[i] != magic[i])
295 {
296 rb->close(fd);
297 return eNotUCL;
298 }
299 }
300
301 // check for supported algorithm
302 if (ucl_header[12] != 0x2E)
303 {
304 rb->close(fd);
305 return eWrongAlgorithm;
306 }
307
308 pHeader->size = Read32(ucl_header + 22); // compressed size
309 if (pHeader->size != filesize - sizeof(ucl_header) - 8)
310 {
311 rb->close(fd);
312 return eMultiBlocks;
313 }
314
315 if (Read32(ucl_header + 18) > pHeader->size) // compare with uncompressed size
316 { // normal case
317 pHeader->flags = 0x00000001; // flags for UCL compressed
318 }
319 else
320 {
321 pHeader->flags = 0x00000000; // very unlikely, content was not compressible
322 }
323
324 // check if we can read the whole file
325 do
326 {
327 read = rb->read(fd, sector, SEC_SIZE);
328 fileread += read;
329 } while (read == SEC_SIZE);
330
331 rb->close(fd);
332
333 if (fileread != filesize)
334 return eReadErr;
335
336 // fill in the hardcoded rest of the header
337 pHeader->destination = ROCKBOX_DEST;
338 pHeader->execute = ROCKBOX_EXEC;
339
340 return eOK;
341}
342
343
344// returns the # of failures, 0 on success
345unsigned ProgramImageFile(char* filename, UINT8* pos, tImageHeader* pImageHeader, int start, int size)
346{
347 int i;
348 int fd;
349 int read; // how many for this sector
350 unsigned failures = 0;
351
352 fd = rb->open(filename, O_RDONLY);
353 if (fd < 0)
354 return false;
355
356 // no error checking necessary here, we checked for minimum size already
357 rb->lseek(fd, start, SEEK_SET); // go to start position
358
359 *(tImageHeader*)sector = *pImageHeader; // copy header into sector buffer
360 read = rb->read(fd, sector + sizeof(tImageHeader), SEC_SIZE - sizeof(tImageHeader)); // payload behind
361 size -= read;
362 read += sizeof(tImageHeader); // to be programmed, but not part of the file
363
364 do
365 {
366 if (!ErazeSector(pos))
367 {
368 // nothing we can do, let the programming count the errors
369 }
370
371 for (i=0; i<read; i++)
372 {
373 if (!ProgramByte(pos + i, sector[i]))
374 {
375 failures++;
376 }
377 }
378
379 pos += SEC_SIZE;
380 read = rb->read(fd, sector, (size > SEC_SIZE) ? SEC_SIZE : size); // payload for next sector
381 size -= read;
382 } while (read > 0);
383
384 rb->close(fd);
385
386 return failures;
387}
388
389// returns the # of failures, 0 on success
390unsigned VerifyImageFile(char* filename, UINT8* pos, tImageHeader* pImageHeader, int start, int size)
391{
392 int i;
393 int fd;
394 int read; // how many for this sector
395 unsigned failures = 0;
396
397 fd = rb->open(filename, O_RDONLY);
398 if (fd < 0)
399 return false;
400
401 // no error checking necessary here, we checked for minimum size already
402 rb->lseek(fd, start, SEEK_SET); // go to start position
403
404 *(tImageHeader*)sector = *pImageHeader; // copy header into sector buffer
405 read = rb->read(fd, sector + sizeof(tImageHeader), SEC_SIZE - sizeof(tImageHeader)); // payload behind
406 size -= read;
407 read += sizeof(tImageHeader); // to be programmed, but not part of the file
408
409 do
410 {
411 for (i=0; i<read; i++)
412 {
413 if (pos[i] != sector[i])
414 {
415 failures++;
416 }
417 }
418
419 pos += SEC_SIZE;
420 read = rb->read(fd, sector, (size > SEC_SIZE) ? SEC_SIZE : size); // payload for next sector
421 size -= read;
422 } while (read);
423
424 rb->close(fd);
425
426 return failures;
427}
428
429
430/***************** User Interface Functions *****************/
431/* (to be changed for Player) */
432
433#ifdef HAVE_LCD_BITMAP
434
435// helper for DoUserDialog()
436void ShowFlashInfo(tFlashInfo* pInfo, tImageHeader* pImageHeader)
437{
438 char buf[32];
439
440 if (!pInfo->manufacturer)
441 {
442 rb->lcd_puts(0, 0, "Flash: M=?? D=??");
443 }
444 else
445 {
446 if (pInfo->size)
447 {
448 rb->snprintf(buf, sizeof(buf), "Flash size: %d KB", pInfo->size / 1024);
449 rb->lcd_puts(0, 0, buf);
450 }
451 else
452 {
453 rb->lcd_puts(0, 0, "Unsupported chip");
454 }
455
456 }
457
458 if (pImageHeader)
459 {
460 rb->snprintf(buf, sizeof(buf), "Image at %d KB", ((UINT8*)pImageHeader - FB) / 1024);
461 rb->lcd_puts(0, 1, buf);
462 }
463 else
464 {
465 rb->lcd_puts(0, 1, "No image found!");
466 }
467
468 rb->lcd_update();
469}
470
471
472// Kind of our main function, defines the application flow.
473void DoUserDialog(void)
474{
475 tImageHeader ImageHeader;
476 tFlashInfo FlashInfo;
477 char buf[32];
478 int button;
479 int rc; // generic return code
480 UINT32 space, aligned_size, true_size;
481 UINT8* pos;
482
483 rb->lcd_setfont(FONT_SYSFIXED);
484
485 pos = (void*)GetSecondImage();
486 rc = GetFlashInfo(&FlashInfo);
487
488 ShowFlashInfo(&FlashInfo, (void*)pos);
489 if (FlashInfo.size == 0) // no valid chip
490 {
491 rb->splash(HZ*3, 0, true, "Not flashable");
492 return; // exit
493 }
494 else if (pos == 0)
495 {
496 rb->splash(HZ*3, 0, true, "No Image");
497 return; // exit
498 }
499
500 rb->lcd_puts(0, 3, "using file:");
501 rb->lcd_puts(0, 4, FILENAME);
502 rb->lcd_puts(0, 6, "[F1] to check file");
503 rb->lcd_puts(0, 7, "other key to exit");
504 rb->lcd_update();
505
506 button = rb->button_get(true);
507 button = rb->button_get(true);
508
509 if (button != BUTTON_F1)
510 {
511 return;
512 }
513
514 rb->lcd_clear_display();
515 rb->lcd_puts(0, 0, "checking...");
516 rb->lcd_update();
517
518 space = FlashInfo.size - (pos-FB + sizeof(ImageHeader)); // size minus start
519 rc = CheckImageFile(FILENAME, space, &ImageHeader);
520 rb->lcd_puts(0, 0, "checked:");
521 switch (rc)
522 {
523 case eOK:
524 rb->lcd_puts(0, 1, "File OK.");
525 break;
526 case eNotUCL:
527 rb->lcd_puts(0, 1, "File not UCL ");
528 rb->lcd_puts(0, 2, "compressed.");
529 rb->lcd_puts(0, 3, "Use uclpack --2e");
530 rb->lcd_puts(0, 4, " --10 rockbox.bin");
531 break;
532 case eWrongAlgorithm:
533 rb->lcd_puts(0, 1, "Wrong algorithm");
534 rb->lcd_puts(0, 2, "for compression.");
535 rb->lcd_puts(0, 3, "Use uclpack --2e");
536 rb->lcd_puts(0, 4, " --10 rockbox.bin");
537 break;
538 case eFileNotFound:
539 rb->lcd_puts(0, 1, "File not found.");
540 rb->lcd_puts(0, 2, "Put this in root:");
541 rb->lcd_puts(0, 4, FILENAME);
542 break;
543 case eTooBig:
544 rb->lcd_puts(0, 1, "File too big,");
545 rb->lcd_puts(0, 2, "won't fit in chip.");
546 break;
547 case eTooSmall:
548 rb->lcd_puts(0, 1, "File too small.");
549 rb->lcd_puts(0, 2, "Incomplete?");
550 break;
551 case eReadErr:
552 rb->lcd_puts(0, 1, "File read error.");
553 break;
554 case eMultiBlocks:
555 rb->lcd_puts(0, 1, "File invalid.");
556 rb->lcd_puts(0, 2, "Blocksize");
557 rb->lcd_puts(0, 3, " too small?");
558 break;
559 default:
560 rb->lcd_puts(0, 1, "Check failed.");
561 break;
562 }
563
564 if (rc == eOK)
565 { // was OK
566 rb->lcd_puts(0, 6, "[F2] to program");
567 rb->lcd_puts(0, 7, "other key to exit");
568 }
569 else
570 { // error occured
571 rb->lcd_puts(0, 6, "Any key to exit");
572 }
573
574 rb->lcd_update();
575
576 button = rb->button_get(true);
577 button = rb->button_get(true);
578
579 if (button != BUTTON_F2)
580 {
581 return;
582 }
583
584 true_size = ImageHeader.size;
585 aligned_size = ((sizeof(tImageHeader) + true_size + SECTORSIZE-1) & ~(SECTORSIZE-1)) - sizeof(tImageHeader); // round up to next flash sector
586 ImageHeader.size = aligned_size; // increase image size such that we reach the next sector
587
588 rb->lcd_clear_display();
589 rb->lcd_puts(0, 0, "Programming...");
590 rb->lcd_update();
591
592 rc = ProgramImageFile(FILENAME, pos, &ImageHeader, UCL_HEADER, true_size);
593 if (rc)
594 { // errors
595 rb->lcd_clear_display();
596 rb->lcd_puts(0, 0, "Error:");
597 rb->lcd_puts(0, 1, "Programming fail!");
598 rb->snprintf(buf, sizeof(buf), "%d errors", rc);
599 rb->lcd_puts(0, 2, buf);
600 rb->lcd_update();
601 button = rb->button_get(true);
602 button = rb->button_get(true);
603 }
604
605 rb->lcd_clear_display();
606 rb->lcd_puts(0, 0, "Verifying...");
607 rb->lcd_update();
608
609 rc = VerifyImageFile(FILENAME, pos, &ImageHeader, UCL_HEADER, true_size);
610
611 rb->lcd_clear_display();
612 if (rc == 0)
613 {
614 rb->lcd_puts(0, 0, "Verify OK.");
615 }
616 else
617 {
618 rb->lcd_puts(0, 0, "Error:");
619 rb->lcd_puts(0, 1, "Verify fail!");
620 rb->snprintf(buf, sizeof(buf), "%d errors", rc);
621 rb->lcd_puts(0, 2, buf);
622 rb->lcd_puts(0, 3, "Use safe image");
623 rb->lcd_puts(0, 4, "if booting hangs:");
624 rb->lcd_puts(0, 5, "F1 during power-on");
625 }
626 rb->lcd_puts(0, 7, "Any key to exit");
627 rb->lcd_update();
628
629 button = rb->button_get(true);
630 button = rb->button_get(true);
631}
632
633#else // HAVE_LCD_BITMAP
634
635// Player implementation has to go here
636void DoUserDialog(void)
637{
638}
639
640#endif // HAVE_LCD_BITMAP
641
642
643/***************** Plugin Entry Point *****************/
644
645enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
646{
647
648
649 /* this macro should be called as the first thing you do in the plugin.
650 it test that the api version and model the plugin was compiled for
651 matches the machine it is running on */
652 TEST_PLUGIN_API(api);
653
654 /* if you don't use the parameter, you can do like
655 this to avoid the compiler warning about it */
656 (void)parameter;
657
658 rb = api; /* copy to global api pointer */
659
660 /* now go ahead and have fun! */
661 DoUserDialog();
662
663 return PLUGIN_OK;
664}