summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiika Pekkarinen <miipekk@ihme.org>2006-08-05 16:11:39 +0000
committerMiika Pekkarinen <miipekk@ihme.org>2006-08-05 16:11:39 +0000
commit85ba65d2a3fa3d10799efadbe3a33f026bf354df (patch)
tree63fc1ab314089455d147eb99113d1e6c7b9a3a49
parent174af79c3182e9e8b57ebb575341e85fd484b888 (diff)
downloadrockbox-85ba65d2a3fa3d10799efadbe3a33f026bf354df.tar.gz
rockbox-85ba65d2a3fa3d10799efadbe3a33f026bf354df.zip
Initial version of the iriver flashing plugin for H1xx. Building of
the code not yet enabled, because the code still lacks some features and safety checks. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@10463 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/plugins/iriver_flash.c569
1 files changed, 569 insertions, 0 deletions
diff --git a/apps/plugins/iriver_flash.c b/apps/plugins/iriver_flash.c
new file mode 100644
index 0000000000..28734d197b
--- /dev/null
+++ b/apps/plugins/iriver_flash.c
@@ -0,0 +1,569 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * !!! DON'T MESS WITH THIS CODE UNLESS YOU'RE ABSOLUTELY SURE WHAT YOU DO !!!
11 *
12 * Copyright (C) 2006 by Miika Pekkarinen
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/* All CFI flash routines are copied and ported from firmware_flash.c */
24
25#ifndef SIMULATOR /* only for target */
26
27unsigned char *audiobuf;
28int audiobuf_size;
29
30#if defined(IRIVER_H120)
31#define PLATFORM_ID ID_IRIVER_H100
32#else
33#undef PLATFORM_ID /* this platform is not (yet) flashable */
34#endif
35
36#ifdef PLATFORM_ID
37
38PLUGIN_HEADER
39
40#if CONFIG_KEYPAD == IRIVER_H100_PAD
41#define KEY1 BUTTON_OFF
42#define KEY2 BUTTON_ON
43#define KEY3 BUTTON_SELECT
44#define KEYNAME1 "[Stop]"
45#define KEYNAME2 "[On]"
46#define KEYNAME3 "[Select]"
47#endif
48
49struct flash_info
50{
51 uint8_t manufacturer;
52 uint8_t id;
53 int size;
54 char name[32];
55};
56
57static struct plugin_api* rb; /* here is a global api struct pointer */
58
59#ifdef IRIVER_H100_SERIES
60#define SEC_SIZE 4096
61#define BOOTLOADER_ERASEGUARD (BOOTLOADER_ENTRYPOINT / SEC_SIZE - 1)
62
63static volatile uint16_t* FB = (uint16_t*)0x00000000; /* Flash base address */
64#endif
65
66/* read the manufacturer and device ID */
67bool cfi_read_id(volatile uint16_t* pBase, uint8_t* pManufacturerID, uint8_t* pDeviceID)
68{
69 uint8_t not_manu, not_id; /* read values before switching to ID mode */
70 uint8_t manu, id; /* read values when in ID mode */
71
72 pBase = (uint16_t*)((uint32_t)pBase & 0xFFF80000); /* down to 512k align */
73
74 /* read the normal content */
75 not_manu = pBase[0]; /* should be 'A' (0x41) and 'R' (0x52) */
76 not_id = pBase[1]; /* from the "ARCH" marker */
77
78 pBase[0x5555] = 0xAA; /* enter command mode */
79 pBase[0x2AAA] = 0x55;
80 pBase[0x5555] = 0x90; /* ID command */
81 rb->sleep(HZ/50); /* Atmel wants 20ms pause here */
82
83 manu = pBase[0];
84 id = pBase[1];
85
86 pBase[0] = 0xF0; /* reset flash (back to normal read mode) */
87 rb->sleep(HZ/50); /* Atmel wants 20ms pause here */
88
89 /* I assume success if the obtained values are different from
90 the normal flash content. This is not perfectly bulletproof, they
91 could theoretically be the same by chance, causing us to fail. */
92 if (not_manu != manu || not_id != id) /* a value has changed */
93 {
94 *pManufacturerID = manu; /* return the results */
95 *pDeviceID = id;
96 return true; /* success */
97 }
98 return false; /* fail */
99}
100
101
102/* erase the sector which contains the given address */
103bool cfi_erase_sector(volatile uint16_t* pAddr)
104{
105 unsigned timeout = 430000; /* the timeout loop should be no less than 25ms */
106
107 FB[0x5555] = 0xAA; /* enter command mode */
108 FB[0x2AAA] = 0x55;
109 FB[0x5555] = 0x80; /* erase command */
110 FB[0x5555] = 0xAA; /* enter command mode */
111 FB[0x2AAA] = 0x55;
112 *pAddr = 0x30; /* erase the sector */
113
114 /* I counted 7 instructions for this loop -> min. 0.58 us per round */
115 /* Plus memory waitstates it will be much more, gives margin */
116 while (*pAddr != 0xFFFF && --timeout); /* poll for erased */
117
118 return (timeout != 0);
119}
120
121
122/* address must be in an erased location */
123inline bool cfi_program_word(volatile uint16_t* pAddr, uint16_t data)
124{
125 unsigned timeout = 85; /* the timeout loop should be no less than 20us */
126
127 if (~*pAddr & data) /* just a safety feature, not really necessary */
128 return false; /* can't set any bit from 0 to 1 */
129
130 FB[0x5555] = 0xAA; /* enter command mode */
131 FB[0x2AAA] = 0x55;
132 FB[0x5555] = 0xA0; /* byte program command */
133
134 *pAddr = data;
135
136 /* I counted 7 instructions for this loop -> min. 0.58 us per round */
137 /* Plus memory waitstates it will be much more, gives margin */
138 while (*pAddr != data && --timeout); /* poll for programmed */
139
140 return (timeout != 0);
141}
142
143
144/* this returns true if supported and fills the info struct */
145bool cfi_get_flash_info(struct flash_info* pInfo)
146{
147 rb->memset(pInfo, 0, sizeof(struct flash_info));
148
149 if (!cfi_read_id(FB, &pInfo->manufacturer, &pInfo->id))
150 return false;
151
152 if (pInfo->manufacturer == 0xBF) /* SST */
153 {
154 if (pInfo->id == 0xD6)
155 {
156 pInfo->size = 256* 1024; /* 256k */
157 rb->strcpy(pInfo->name, "SST39VF020");
158 return true;
159 }
160 else if (pInfo->id == 0xD7)
161 {
162 pInfo->size = 512* 1024; /* 512k */
163 rb->strcpy(pInfo->name, "SST39VF040");
164 return true;
165 }
166 else if (pInfo->id == 0x82)
167 {
168 pInfo->size = 2048* 1024; /* 2 MiB */
169 rb->strcpy(pInfo->name, "SST39VF160");
170 return true;
171 }
172 else
173 return false;
174 }
175 return false;
176}
177
178
179/*********** Utility Functions ************/
180
181
182/* Tool function to calculate a CRC32 across some buffer */
183/* third argument is either 0xFFFFFFFF to start or value from last piece */
184unsigned crc_32(unsigned char* buf, unsigned len, unsigned crc32)
185{
186 /* CCITT standard polynomial 0x04C11DB7 */
187 static const unsigned crc32_lookup[16] =
188 { /* lookup table for 4 bits at a time is affordable */
189 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9,
190 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005,
191 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61,
192 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD
193 };
194
195 unsigned char byte;
196 unsigned t;
197
198 while (len--)
199 {
200 byte = *buf++; /* get one byte of data */
201
202 /* upper nibble of our data */
203 t = crc32 >> 28; /* extract the 4 most significant bits */
204 t ^= byte >> 4; /* XOR in 4 bits of data into the extracted bits */
205 crc32 <<= 4; /* shift the CRC register left 4 bits */
206 crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */
207
208 /* lower nibble of our data */
209 t = crc32 >> 28; /* extract the 4 most significant bits */
210 t ^= byte & 0x0F; /* XOR in 4 bits of data into the extracted bits */
211 crc32 <<= 4; /* shift the CRC register left 4 bits */
212 crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */
213 }
214
215 return crc32;
216}
217
218
219/***************** User Interface Functions *****************/
220int WaitForButton(void)
221{
222 int button;
223
224 do
225 {
226 button = rb->button_get(true);
227 } while (button & BUTTON_REL);
228
229 return button;
230}
231
232/* helper for DoUserDialog() */
233void ShowFlashInfo(struct flash_info* pInfo)
234{
235 char buf[32];
236
237 if (!pInfo->manufacturer)
238 {
239 rb->lcd_puts(0, 0, "Flash: M=?? D=??");
240 rb->lcd_puts(0, 1, "Impossible to program");
241 }
242 else
243 {
244 rb->snprintf(buf, sizeof(buf), "Flash: M=%02x D=%02x",
245 pInfo->manufacturer, pInfo->id);
246 rb->lcd_puts(0, 0, buf);
247
248
249 if (pInfo->size)
250 {
251 rb->lcd_puts(0, 1, pInfo->name);
252 rb->snprintf(buf, sizeof(buf), "Size: %d KB", pInfo->size / 1024);
253 rb->lcd_puts(0, 2, buf);
254 }
255 else
256 {
257 rb->lcd_puts(0, 1, "Unsupported chip");
258 }
259
260 }
261
262 rb->lcd_update();
263}
264
265int load_firmware_file(const char *filename, uint32_t *checksum)
266{
267 int fd;
268 int len, rc;
269 int i;
270 uint32_t sum;
271
272 fd = rb->open(filename, O_RDONLY);
273 if (fd < 0)
274 return -1;
275
276 len = rb->filesize(fd);
277
278 if (audiobuf_size < len)
279 {
280 rb->splash(HZ*3, true, "Out of memory!");
281 rb->close(fd);
282 return -2;
283 }
284
285 rb->read(fd, checksum, 4);
286 rb->lseek(fd, FIRMWARE_OFFSET_FILE_DATA, SEEK_SET);
287 len -= FIRMWARE_OFFSET_FILE_DATA;
288
289 rc = rb->read(fd, audiobuf, len);
290 rb->close(fd);
291 if (rc != len)
292 {
293 rb->splash(HZ*3, true, "Read failure");
294 return -3;
295 }
296
297 /* Verify the checksum */
298 sum = 0;
299 for (i = 0; i < len; i++)
300 sum += audiobuf[i];
301
302 if (sum != *checksum)
303 {
304 rb->splash(HZ*3, true, "Checksums mismatch!");
305 return -4;
306 }
307
308 return len;
309}
310
311int flash_rockbox(const char *filename)
312{
313 struct flash_header hdr;
314 char buf[32];
315 int pos, i, len, rc;
316 unsigned long checksum, sum;
317 unsigned char *p8;
318 uint16_t *p16;
319
320 len = load_firmware_file(filename, &checksum);
321 if (len < 0)
322 return len * 10;
323
324 /* Erase the program flash. */
325 for (i = 1; i < BOOTLOADER_ERASEGUARD && (i-1)*4096 < len + 32; i++)
326 {
327 rc = cfi_erase_sector(FB + (SEC_SIZE/2) * i);
328 rb->snprintf(buf, sizeof(buf), "Erase: 0x%03x (%d)", i, rc);
329 rb->lcd_puts(0, 3, buf);
330 rb->lcd_update();
331 }
332
333 /* Write the magic and size. */
334 rb->memset(&hdr, 0, sizeof(struct flash_header));
335 hdr.magic = FLASH_MAGIC;
336 hdr.length = len;
337 // rb->strncpy(hdr.version, APPSVERSION, sizeof(hdr.version)-1);
338 p16 = (uint16_t *)&hdr;
339
340 rb->snprintf(buf, sizeof(buf), "Programming");
341 rb->lcd_puts(0, 4, buf);
342 rb->lcd_update();
343
344 pos = FLASH_ENTRYPOINT/2;
345 for (i = 0; i < (long)sizeof(struct flash_header)/2; i++)
346 {
347 cfi_program_word(FB + pos, p16[i]);
348 pos++;
349 }
350
351 p16 = (uint16_t *)audiobuf;
352 for (i = 0; i < len/2 && pos < (BOOTLOADER_ENTRYPOINT/2); i++)
353 cfi_program_word(FB + pos + i, p16[i]);
354
355 /* Verify */
356 p8 = (char *)FLASH_ENTRYPOINT;
357 p8 += sizeof(struct flash_header);
358 sum = 0;
359 for (i = 0; i < len; i++)
360 sum += p8[i];
361
362 if (sum != checksum)
363 {
364 rb->splash(HZ*3, true, "Verify failed!");
365 /* Erase the magic sector so bootloader does not try to load
366 * rockbox from flash and crash. */
367 cfi_erase_sector(FB + SEC_SIZE/2);
368 return -5;
369 }
370
371 return 0;
372}
373
374void show_fatal_error(void)
375{
376 rb->splash(HZ*30, true, "Disable idle poweroff, connect AC power and DON'T TURN PLAYER OFF!!");
377 rb->splash(HZ*30, true, "Contact Rockbox developers as soon as possible!");
378 rb->splash(HZ*30, true, "Your device won't be bricked unless you turn off the power");
379 rb->splash(HZ*30, true, "Don't use the device before further instructions from Rockbox developers");
380}
381
382int flash_bootloader(const char *filename)
383{
384 // char buf[32];
385 int pos, i, len, rc;
386 unsigned long checksum, sum, crc32;
387 unsigned char *p8;
388 uint16_t *p16;
389
390 len = load_firmware_file(filename, &checksum);
391 if (len < 0)
392 return len * 10;
393
394 if (len > 0xFFFF)
395 {
396 rb->splash(HZ*3, true, "Too big bootloader");
397 return -1;
398 }
399
400 /* Verify the crc32 checksum also. */
401 crc32 = crc_32(audiobuf, len, 0xffffffff);
402 // rb->snprintf(buf, sizeof buf, "crc32 = 0x%08x", crc32);
403 // rb->splash(HZ*10, true, buf);
404
405 if (crc32 != 0x5361a679)
406 {
407 rb->splash(HZ*3, true, "Untested bootloader");
408 return -2;
409 }
410
411 rb->lcd_puts(0, 3, "Processing critical sections...");
412 rb->lcd_update();
413
414 /* Erase the boot sector and write a proper reset vector. */
415 cfi_erase_sector(FB);
416 p16 = (uint16_t *)audiobuf;
417 for (i = 0; i < 4; i++)
418 cfi_program_word(FB + i, p16[i]);
419
420 /* Erase the bootloader flash section. */
421 for (i = BOOTLOADER_ENTRYPOINT/SEC_SIZE; i < 0x200; i++)
422 rc = cfi_erase_sector(FB + (SEC_SIZE/2) * i);
423
424 pos = BOOTLOADER_ENTRYPOINT/2;
425 p16 = (uint16_t *)audiobuf;
426 for (i = 0; i < len/2; i++)
427 cfi_program_word(FB + pos + i, p16[i]);
428
429 /* Verify */
430 p8 = (char *)BOOTLOADER_ENTRYPOINT;
431 sum = 0;
432 for (i = 0; i < len; i++)
433 sum += p8[i];
434
435 if (sum != checksum)
436 {
437 rb->splash(HZ*3, true, "Verify failed!");
438 show_fatal_error();
439 return -5;
440 }
441
442 p8 = (char *)FB;
443 for (i = 0; i < 8; i++)
444 {
445 if (p8[i] != audiobuf[i])
446 {
447 rb->splash(HZ*3, true, "Bootvector corrupt!");
448 show_fatal_error();
449 break;
450 }
451 }
452
453 return 0;
454}
455
456/* Kind of our main function, defines the application flow. */
457void DoUserDialog(char* filename)
458{
459 struct flash_info fi;
460 int rc; /* generic return code */
461
462 /* this can only work if Rockbox runs in DRAM, not flash ROM */
463 if ((uint16_t*)rb >= FB && (uint16_t*)rb < FB + 4096*1024) /* 4 MB max */
464 { /* we're running from flash */
465 rb->splash(HZ*3, true, "Not from ROM");
466 return; /* exit */
467 }
468
469 /* refuse to work if the power may fail meanwhile */
470 if (!rb->battery_level_safe())
471 {
472 rb->splash(HZ*3, true, "Battery too low!");
473 return; /* exit */
474 }
475
476 rb->lcd_setfont(FONT_SYSFIXED);
477
478 rc = cfi_get_flash_info(&fi);
479 ShowFlashInfo(&fi);
480 if (fi.size == 0) /* no valid chip */
481 {
482 rb->splash(HZ*3, true, "Sorry!");
483 return; /* exit */
484 }
485
486 /* Debug? */
487#if 0
488 rb->memcpy(&hdr, (uint8_t *)(FLASH_ENTRYPOINT), sizeof(struct flash_header));
489 rb->snprintf(buf, sizeof(buf), "Magic: 0x%03x", hdr.magic);
490 rb->lcd_puts(0, 3, buf);
491 rb->snprintf(buf, sizeof(buf), "Size: 0x%03x", hdr.length);
492 rb->lcd_puts(0, 4, buf);
493 rb->lcd_update();
494 rb->sleep(HZ*10);
495
496 rb->memcpy(&hdr, (uint8_t *)(FLASH_ENTRYPOINT/2), sizeof(struct flash_header));
497 rb->snprintf(buf, sizeof(buf), "Magic: 0x%03x", hdr.magic);
498 rb->lcd_puts(0, 3, buf);
499 rb->snprintf(buf, sizeof(buf), "Size: 0x%03x", hdr.length);
500 rb->lcd_puts(0, 4, buf);
501 rb->lcd_update();
502 rb->sleep(HZ*10);
503#endif
504
505 /* Restore? */
506#if 0
507 fd = rb->open("/internal_rom_000000-1FFFFF.bin", O_RDONLY);
508 if (fd < 0)
509 return ;
510 len = rb->filesize(fd);
511
512 /* Erase the program flash. */
513 for (i = 1; i < 0x1EF; i++)
514 {
515 rc = cfi_erase_sector(FB + (SEC_SIZE/2) * i);
516 rb->snprintf(buf, sizeof(buf), "Erase: 0x%03x (%d)", i, rc);
517 rb->lcd_puts(0, 3, buf);
518 rb->lcd_update();
519 }
520
521 i = FLASH_ENTRYPOINT/2;
522 rb->lseek(fd, i*2, SEEK_SET);
523 len -= i*2 - 0xffff;
524 for (; len > 0 && i < (0x1F0000/2); i++)
525 {
526 rb->read(fd, bytes, 2);
527 cfi_program_word(FB + i, (bytes[0] << 8) | bytes[1]);
528 len -= 2;
529 }
530
531 rb->close(fd);
532 return ;
533#endif
534
535 if (filename == NULL)
536 {
537 rb->splash(HZ*3, true, "Please use this plugin with \"Open with...\"");
538 return ;
539 }
540
541 audiobuf = rb->plugin_get_audio_buffer(&audiobuf_size);
542
543 if (rb->strcasestr(filename, "/rockbox.iriver"))
544 flash_rockbox(filename);
545 else if (rb->strcasestr(filename, "/bootloader.iriver"))
546 flash_bootloader(filename);
547 else
548 rb->splash(HZ*3, true, "Unknown file type");
549}
550
551
552/***************** Plugin Entry Point *****************/
553
554enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
555{
556 int oldmode;
557
558 rb = api; /* copy to global api pointer */
559
560 /* now go ahead and have fun! */
561 oldmode = rb->system_memory_guard(MEMGUARD_NONE); /*disable memory guard */
562 DoUserDialog((char*) parameter);
563 rb->system_memory_guard(oldmode); /* re-enable memory guard */
564
565 return PLUGIN_OK;
566}
567
568#endif /* ifdef PLATFORM_ID */
569#endif /* #ifndef SIMULATOR */