| /*************************************************************************** |
| * __________ __ ___. |
| * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| * \/ \/ \/ \/ \/ |
| * $Id$ |
| * |
| * Plugin for reprogramming the whole Flash ROM chip with a new content. |
| * !!! DON'T MESS WITH THIS CODE UNLESS YOU'RE ABSOLUTELY SURE WHAT YOU DO !!! |
| * |
| * Copyright (C) 2003 Jörg Hohensohn [IDC]Dragon |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version 2 |
| * of the License, or (at your option) any later version. |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| * KIND, either express or implied. |
| * |
| ****************************************************************************/ |
| #include "plugin.h" |
| |
| #ifndef SIMULATOR /* only for target */ |
| |
| /* define DUMMY if you only want to "play" with the UI, does no harm */ |
| /* #define DUMMY */ |
| |
| #ifndef UINT8 |
| #define UINT8 unsigned char |
| #endif |
| |
| #ifndef UINT16 |
| #define UINT16 unsigned short |
| #endif |
| |
| #ifndef UINT32 |
| #define UINT32 unsigned long |
| #endif |
| |
| /* platform IDs as I have used them in my firmware templates */ |
| #define ID_RECORDER 0 |
| #define ID_FM 1 |
| #define ID_PLAYER 2 |
| #define ID_REC_V2 3 |
| #define ID_ONDIO_FM 4 |
| #define ID_ONDIO_SP 5 |
| |
| /* Here I have to check for ARCHOS_* defines in source code, which is |
| generally strongly discouraged. But here I'm not checking for a certain |
| feature, I'm checking for the model itself. */ |
| #if defined(ARCHOS_PLAYER) |
| #define FILE_TYPE "player" |
| #define KEEP VERSION_ADR /* keep the firmware version */ |
| #define PLATFORM_ID ID_PLAYER |
| #elif defined(ARCHOS_RECORDER) |
| #define FILE_TYPE "rec" |
| #define KEEP MASK_ADR /* keep the mask value */ |
| #define PLATFORM_ID ID_RECORDER |
| #elif defined(ARCHOS_RECORDERV2) |
| #define FILE_TYPE "v2" |
| #define KEEP MASK_ADR /* keep the mask value */ |
| #define PLATFORM_ID ID_REC_V2 |
| #elif defined(ARCHOS_FMRECORDER) |
| #define FILE_TYPE "fm" |
| #define KEEP MASK_ADR /* keep the mask value */ |
| #define PLATFORM_ID ID_FM |
| #elif defined(ARCHOS_ONDIOFM) |
| #define FILE_TYPE "ondiofm" |
| #define KEEP MASK_ADR /* keep the mask value */ |
| #define PLATFORM_ID ID_ONDIO_FM |
| #elif defined(ARCHOS_ONDIOSP) |
| #define FILE_TYPE "ondiosp" |
| #define KEEP MASK_ADR /* keep the mask value */ |
| #define PLATFORM_ID ID_ONDIO_SP |
| #else |
| #undef PLATFORM_ID /* this platform is not (yet) flashable */ |
| #endif |
| |
| #ifdef PLATFORM_ID |
| |
| PLUGIN_HEADER |
| |
| #if CONFIG_KEYPAD == ONDIO_PAD /* limited keypad */ |
| #define KEY1 BUTTON_LEFT |
| #define KEY2 BUTTON_UP |
| #define KEY3 BUTTON_RIGHT |
| #define KEYNAME1 "[Left]" |
| #define KEYNAME2 "[Up]" |
| #define KEYNAME3 "[Right]" |
| #else /* recorder keypad */ |
| #define KEY1 BUTTON_F1 |
| #define KEY2 BUTTON_F2 |
| #define KEY3 BUTTON_F3 |
| #define KEYNAME1 "[F1]" |
| #define KEYNAME2 "[F2]" |
| #define KEYNAME3 "[F3]" |
| #endif |
| |
| /* result of the CheckFirmwareFile() function */ |
| typedef enum |
| { |
| eOK = 0, |
| eFileNotFound, /* errors from here on */ |
| eTooBig, |
| eTooSmall, |
| eReadErr, |
| eBadContent, |
| eCrcErr, |
| eBadPlatform, |
| } tCheckResult; |
| |
| /* result of the CheckBootROM() function */ |
| typedef enum |
| { |
| eBootROM, /* the supported boot ROM(s) */ |
| eUnknown, /* unknown boot ROM */ |
| eROMless, /* flash mapped to zero */ |
| } tCheckROM; |
| |
| typedef struct |
| { |
| UINT8 manufacturer; |
| UINT8 id; |
| int size; |
| char name[32]; |
| } tFlashInfo; |
| |
| static const struct plugin_api* rb; /* here is a global api struct pointer */ |
| |
| #define MASK_ADR 0xFC /* position of hardware mask value in Flash */ |
| #define VERSION_ADR 0xFE /* position of firmware version value in Flash */ |
| #define PLATFORM_ADR 0xFB /* position of my platform ID value in Flash */ |
| #define SEC_SIZE 4096 /* size of one flash sector */ |
| static UINT8* sector; /* better not place this on the stack... */ |
| static volatile UINT8* FB = (UINT8*)0x02000000; /* Flash base address */ |
| |
| |
| /***************** Flash Functions *****************/ |
| |
| |
| /* read the manufacturer and device ID */ |
| bool ReadID(volatile UINT8* pBase, UINT8* pManufacturerID, UINT8* pDeviceID) |
| { |
| UINT8 not_manu, not_id; /* read values before switching to ID mode */ |
| UINT8 manu, id; /* read values when in ID mode */ |
| |
| pBase = (UINT8*)((UINT32)pBase & 0xFFF80000); /* down to 512k align */ |
| |
| /* read the normal content */ |
| not_manu = pBase[0]; /* should be 'A' (0x41) and 'R' (0x52) */ |
| not_id = pBase[1]; /* from the "ARCH" marker */ |
| |
| pBase[0x5555] = 0xAA; /* enter command mode */ |
| pBase[0x2AAA] = 0x55; |
| pBase[0x5555] = 0x90; /* ID command */ |
| rb->sleep(HZ/50); /* Atmel wants 20ms pause here */ |
| |
| manu = pBase[0]; |
| id = pBase[1]; |
| |
| pBase[0] = 0xF0; /* reset flash (back to normal read mode) */ |
| rb->sleep(HZ/50); /* Atmel wants 20ms pause here */ |
| |
| /* I assume success if the obtained values are different from |
| the normal flash content. This is not perfectly bulletproof, they |
| could theoretically be the same by chance, causing us to fail. */ |
| if (not_manu != manu || not_id != id) /* a value has changed */ |
| { |
| *pManufacturerID = manu; /* return the results */ |
| *pDeviceID = id; |
| return true; /* success */ |
| } |
| return false; /* fail */ |
| } |
| |
| |
| /* erase the sector which contains the given address */ |
| bool EraseSector(volatile UINT8* pAddr) |
| { |
| #ifdef DUMMY |
| (void)pAddr; /* prevents warning */ |
| return true; |
| #else |
| volatile UINT8* pBase = (UINT8*)((UINT32)pAddr & 0xFFF80000); /* round down to 512k align */ |
| unsigned timeout = 43000; /* the timeout loop should be no less than 25ms */ |
| |
| pBase[0x5555] = 0xAA; /* enter command mode */ |
| pBase[0x2AAA] = 0x55; |
| pBase[0x5555] = 0x80; /* erase command */ |
| pBase[0x5555] = 0xAA; /* enter command mode */ |
| pBase[0x2AAA] = 0x55; |
| *pAddr = 0x30; /* erase the sector */ |
| |
| /* I counted 7 instructions for this loop -> min. 0.58 us per round */ |
| /* Plus memory waitstates it will be much more, gives margin */ |
| while (*pAddr != 0xFF && --timeout); /* poll for erased */ |
| |
| return (timeout != 0); |
| #endif |
| } |
| |
| |
| /* address must be in an erased location */ |
| inline bool ProgramByte(volatile UINT8* pAddr, UINT8 data) |
| { |
| #ifdef DUMMY |
| (void)pAddr; /* prevents warnings */ |
| (void)data; |
| return true; |
| #else |
| unsigned timeout = 35; /* the timeout loop should be no less than 20us */ |
| |
| if (~*pAddr & data) /* just a safety feature, not really necessary */ |
| return false; /* can't set any bit from 0 to 1 */ |
| |
| FB[0x5555] = 0xAA; /* enter command mode */ |
| FB[0x2AAA] = 0x55; |
| FB[0x5555] = 0xA0; /* byte program command */ |
| |
| *pAddr = data; |
| |
| /* I counted 7 instructions for this loop -> min. 0.58 us per round */ |
| /* Plus memory waitstates it will be much more, gives margin */ |
| while (*pAddr != data && --timeout); /* poll for programmed */ |
| |
| return (timeout != 0); |
| #endif |
| } |
| |
| |
| /* this returns true if supported and fills the info struct */ |
| bool GetFlashInfo(tFlashInfo* pInfo) |
| { |
| rb->memset(pInfo, 0, sizeof(tFlashInfo)); |
| |
| if (!ReadID(FB, &pInfo->manufacturer, &pInfo->id)) |
| return false; |
| |
| if (pInfo->manufacturer == 0xBF) /* SST */ |
| { |
| if (pInfo->id == 0xD6) |
| { |
| pInfo->size = 256* 1024; /* 256k */ |
| rb->strcpy(pInfo->name, "SST39VF020"); |
| return true; |
| } |
| else if (pInfo->id == 0xD7) |
| { |
| pInfo->size = 512* 1024; /* 512k */ |
| rb->strcpy(pInfo->name, "SST39VF040"); |
| return true; |
| } |
| else |
| return false; |
| } |
| return false; |
| } |
| |
| |
| /*********** Utility Functions ************/ |
| |
| |
| /* Tool function to calculate a CRC32 across some buffer */ |
| /* third argument is either 0xFFFFFFFF to start or value from last piece */ |
| unsigned crc_32(unsigned char* buf, unsigned len, unsigned crc32) |
| { |
| /* CCITT standard polynomial 0x04C11DB7 */ |
| static const unsigned crc32_lookup[16] = |
| { /* lookup table for 4 bits at a time is affordable */ |
| 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, |
| 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005, |
| 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61, |
| 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD |
| }; |
| |
| unsigned char byte; |
| unsigned t; |
| |
| while (len--) |
| { |
| byte = *buf++; /* get one byte of data */ |
| |
| /* upper nibble of our data */ |
| t = crc32 >> 28; /* extract the 4 most significant bits */ |
| t ^= byte >> 4; /* XOR in 4 bits of data into the extracted bits */ |
| crc32 <<= 4; /* shift the CRC register left 4 bits */ |
| crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */ |
| |
| /* lower nibble of our data */ |
| t = crc32 >> 28; /* extract the 4 most significant bits */ |
| t ^= byte & 0x0F; /* XOR in 4 bits of data into the extracted bits */ |
| crc32 <<= 4; /* shift the CRC register left 4 bits */ |
| crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */ |
| } |
| |
| return crc32; |
| } |
| |
| |
| /*********** Firmware File Functions + helpers ************/ |
| |
| /* test if the version number is consistent with the platform */ |
| bool CheckPlatform(int platform_id, UINT16 version) |
| { |
| if (version == 200) |
| { /* for my very first firmwares, I foolishly changed it to 200 */ |
| return (platform_id == ID_RECORDER || platform_id == ID_FM); |
| } |
| else if (version == 123) |
| { /* it can be a FM or V2 recorder */ |
| return (platform_id == ID_FM || platform_id == ID_REC_V2); |
| } |
| else if (version == 132) |
| { /* newer Ondio, and seen on a V2 recorder */ |
| return (platform_id == ID_ONDIO_SP || platform_id == ID_ONDIO_FM |
| || platform_id == ID_REC_V2); |
| } |
| else if (version == 104) |
| { /* classic Ondio128 */ |
| return (platform_id == ID_ONDIO_FM); |
| } |
| else if (version >= 115 && version <= 129) |
| { /* the range of Recorders seen so far */ |
| return (platform_id == ID_RECORDER); |
| } |
| else if (version == 0 || (version >= 300 && version <= 508)) |
| { /* for very old players, I've seen zero */ |
| return (platform_id == ID_PLAYER); |
| } |
| |
| return false; /* unknown */ |
| } |
| |
| |
| tCheckResult CheckFirmwareFile(char* filename, int chipsize, bool is_romless) |
| { |
| int i; |
| int fd; |
| int fileleft; /* size info, how many left for reading */ |
| int fileread = 0; /* total size as read from the file */ |
| int read_now; /* how many to read for this sector */ |
| int got_now; /* how many gotten for this sector */ |
| unsigned crc32 = 0xFFFFFFFF; /* CCITT init value */ |
| unsigned file_crc; /* CRC value read from file */ |
| bool has_crc; |
| |
| fd = rb->open(filename, O_RDONLY); |
| if (fd < 0) |
| return eFileNotFound; |
| |
| fileleft = rb->filesize(fd); |
| if (fileleft > chipsize) |
| { |
| rb->close(fd); |
| return eTooBig; |
| } |
| else if (fileleft < 20000) /* give it some reasonable lower limit */ |
| { |
| rb->close(fd); |
| return eTooSmall; |
| } |
| |
| if (fileleft == 256*1024) |
| { /* original dumped firmware file has no CRC nor platform ID */ |
| has_crc = false; |
| } |
| else |
| { |
| has_crc = true; |
| fileleft -= sizeof(unsigned); /* exclude the last 4 bytes */ |
| } |
| |
| /* do some sanity checks */ |
| |
| got_now = rb->read(fd, sector, SEC_SIZE); /* read first sector */ |
| fileread += got_now; |
| fileleft -= got_now; |
| if (got_now != SEC_SIZE) |
| { |
| rb->close(fd); |
| return eReadErr; |
| } |
| |
| /* version number in file plausible with this hardware? */ |
| if (!CheckPlatform(PLATFORM_ID, *(UINT16*)(sector + VERSION_ADR))) |
| { |
| rb->close(fd); |
| return eBadPlatform; |
| } |
| |
| if (has_crc) |
| { |
| crc32 = crc_32(sector, SEC_SIZE, crc32); /* checksum */ |
| |
| /* in addition to the CRC, my files also have a platform ID */ |
| if (sector[PLATFORM_ADR] != PLATFORM_ID) /* for our hardware? */ |
| { |
| rb->close(fd); |
| return eBadPlatform; |
| } |
| } |
| |
| if (is_romless) |
| { /* in this case, there is not much we can check */ |
| if (*(UINT32*)sector != 0x00000200) /* reset vector */ |
| { |
| rb->close(fd); |
| return eBadContent; |
| } |
| } |
| else |
| { |
| /* compare some bytes which have to be identical */ |
| if (*(UINT32*)sector != 0x41524348) /* "ARCH" */ |
| { |
| rb->close(fd); |
| return eBadContent; |
| } |
| |
| for (i = 0x30; i<MASK_ADR-2; i++) /* leave two bytes for me */ |
| { |
| if (sector[i] != FB[i]) |
| { |
| rb->close(fd); |
| return eBadContent; |
| } |
| } |
| } |
| |
| /* check if we can read the whole file, and do checksum */ |
| do |
| { |
| read_now = MIN(SEC_SIZE, fileleft); |
| got_now = rb->read(fd, sector, read_now); |
| fileread += got_now; |
| fileleft -= got_now; |
| |
| if (read_now != got_now) |
| { |
| rb->close(fd); |
| return eReadErr; |
| } |
| |
| if (has_crc) |
| { |
| crc32 = crc_32(sector, got_now, crc32); /* checksum */ |
| } |
| } while (fileleft); |
| |
| if (has_crc) |
| { |
| got_now = rb->read(fd, &file_crc, sizeof(file_crc)); |
| if (got_now != sizeof(file_crc)) |
| { |
| rb->close(fd); |
| return eReadErr; |
| } |
| } |
| |
| /* must be EOF now */ |
| got_now = rb->read(fd, sector, SEC_SIZE); |
| rb->close(fd); |
| if (got_now != 0) |
| return eReadErr; |
| |
| if (has_crc && file_crc != crc32) |
| return eCrcErr; |
| |
| return eOK; |
| } |
| |
| |
| /* returns the # of failures, 0 on success */ |
| unsigned ProgramFirmwareFile(char* filename, int chipsize) |
| { |
| int i, j; |
| int fd; |
| int read = SEC_SIZE; /* how many for this sector */ |
| UINT16 keep = *(UINT16*)(FB + KEEP); /* we must keep this! */ |
| unsigned failures = 0; |
| |
| fd = rb->open(filename, O_RDONLY); |
| if (fd < 0) |
| return false; |
| |
| for (i=0; i<chipsize; i+=SEC_SIZE) |
| { |
| if (!EraseSector(FB + i)) |
| { |
| /* nothing we can do, let the programming count the errors */ |
| } |
| |
| if (read == SEC_SIZE) /* not EOF yet */ |
| { |
| read = rb->read(fd, sector, SEC_SIZE); |
| if (i==0) |
| { /* put original value back in */ |
| *(UINT16*)(sector + KEEP) = keep; |
| } |
| |
| for (j=0; j<read; j++) |
| { |
| if (!ProgramByte(FB + i + j, sector[j])) |
| { |
| failures++; |
| } |
| } |
| } |
| } |
| |
| rb->close(fd); |
| |
| return failures; |
| } |
| |
| |
| /* returns the # of failures, 0 on success */ |
| unsigned VerifyFirmwareFile(char* filename) |
| { |
| int i=0, j; |
| int fd; |
| int read = SEC_SIZE; /* how many for this sector */ |
| unsigned failures = 0; |
| |
| fd = rb->open(filename, O_RDONLY); |
| if (fd < 0) |
| return false; |
| |
| do |
| { |
| read = rb->read(fd, sector, SEC_SIZE); |
| |
| for (j=0; j<read; j++) |
| { |
| /* position of keep value is no error */ |
| if (FB[i] != sector[j] && i != KEEP && i != (KEEP+1)) |
| { |
| failures++; |
| } |
| i++; |
| } |
| } |
| while (read == SEC_SIZE); |
| |
| rb->close(fd); |
| |
| return failures; |
| } |
| |
| |
| /***************** Support Functions *****************/ |
| |
| /* check if we have "normal" boot ROM or flash mirrored to zero */ |
| tCheckROM CheckBootROM(void) |
| { |
| unsigned boot_crc; |
| unsigned* pFlash = (unsigned*)FB; |
| unsigned* pRom = (unsigned*)0x0; |
| unsigned i; |
| |
| boot_crc = crc_32((unsigned char*)0x0, 64*1024, 0xFFFFFFFF); |
| if (boot_crc == 0x56DBA4EE /* the known boot ROM */ |
| #if PLATFORM_ID == ID_PLAYER |
| /* alternative boot ROM found in one single player so far */ |
| || boot_crc == 0x358099E8 |
| #endif |
| ) |
| return eBootROM; |
| |
| /* check if ROM is a flash mirror */ |
| for (i=0; i<256*1024/sizeof(unsigned); i++) |
| { |
| if (*pRom++ != *pFlash++) |
| { /* difference means no mirror */ |
| return eUnknown; |
| } |
| } |
| |
| return eROMless; |
| } |
| |
| |
| /***************** User Interface Functions *****************/ |
| |
| int WaitForButton(void) |
| { |
| int button; |
| |
| do |
| { |
| button = rb->button_get(true); |
| } while (button & BUTTON_REL); |
| |
| return button; |
| } |
| |
| #ifdef HAVE_LCD_BITMAP |
| /* Recorder implementation */ |
| |
| /* helper for DoUserDialog() */ |
| void ShowFlashInfo(tFlashInfo* pInfo) |
| { |
| char buf[32]; |
| |
| if (!pInfo->manufacturer) |
| { |
| rb->lcd_puts(0, 0, "Flash: M=?? D=??"); |
| rb->lcd_puts(0, 1, "Impossible to program"); |
| } |
| else |
| { |
| rb->snprintf(buf, sizeof(buf), "Flash: M=%02x D=%02x", |
| pInfo->manufacturer, pInfo->id); |
| rb->lcd_puts(0, 0, buf); |
| |
| |
| if (pInfo->size) |
| { |
| rb->lcd_puts(0, 1, pInfo->name); |
| rb->snprintf(buf, sizeof(buf), "Size: %d KB", pInfo->size / 1024); |
| rb->lcd_puts(0, 2, buf); |
| } |
| else |
| { |
| rb->lcd_puts(0, 1, "Unsupported chip"); |
| } |
| |
| } |
| |
| rb->lcd_update(); |
| } |
| |
| |
| /* Kind of our main function, defines the application flow. */ |
| void DoUserDialog(char* filename) |
| { |
| tFlashInfo FlashInfo; |
| char buf[32]; |
| char default_filename[32]; |
| int button; |
| int rc; /* generic return code */ |
| ssize_t memleft; |
| tCheckROM result; |
| bool is_romless; |
| |
| /* this can only work if Rockbox runs in DRAM, not flash ROM */ |
| if ((UINT8*)rb >= FB && (UINT8*)rb < FB + 4096*1024) /* 4 MB max */ |
| { /* we're running from flash */ |
| rb->splash(HZ*3, "Not from ROM"); |
| return; /* exit */ |
| } |
| |
| /* test if the user is running the correct plugin for this box */ |
| if (!CheckPlatform(PLATFORM_ID, *(UINT16*)(FB + VERSION_ADR))) |
| { |
| rb->splash(HZ*3, "Wrong plugin"); |
| return; /* exit */ |
| } |
| |
| /* refuse to work if the power may fail meanwhile */ |
| if (!rb->battery_level_safe()) |
| { |
| rb->splash(HZ*3, "Battery too low!"); |
| return; /* exit */ |
| } |
| |
| /* check boot ROM */ |
| result = CheckBootROM(); |
| if (result == eUnknown) |
| { /* no support for any other yet */ |
| rb->splash(HZ*3, "Wrong boot ROM"); |
| return; /* exit */ |
| } |
| is_romless = (result == eROMless); |
| |
| /* compose filename if none given */ |
| if (filename == NULL) |
| { |
| rb->snprintf( |
| default_filename, |
| sizeof(default_filename), |
| "/firmware_%s%s.bin", |
| FILE_TYPE, |
| is_romless ? "_norom" : ""); |
| filename = default_filename; |
| } |
| |
| /* "allocate" memory */ |
| sector = rb->plugin_get_buffer((size_t *)&memleft); |
| if (memleft < SEC_SIZE) /* need buffer for a flash sector */ |
| { |
| rb->splash(HZ*3, "Out of memory"); |
| return; /* exit */ |
| } |
| |
| rb->lcd_setfont(FONT_SYSFIXED); |
| |
| rc = GetFlashInfo(&FlashInfo); |
| ShowFlashInfo(&FlashInfo); |
| if (FlashInfo.size == 0) /* no valid chip */ |
| { |
| rb->splash(HZ*3, "Sorry!"); |
| return; /* exit */ |
| } |
| |
| rb->lcd_puts(0, 3, "using file:"); |
| rb->lcd_puts_scroll(0, 4, filename); |
| rb->lcd_puts(0, 6, KEYNAME1 " to check file"); |
| rb->lcd_puts(0, 7, "other key to exit"); |
| rb->lcd_update(); |
| |
| button = WaitForButton(); |
| if (button != KEY1) |
| { |
| return; |
| } |
| |
| rb->lcd_clear_display(); |
| rb->lcd_puts(0, 0, "checking..."); |
| rb->lcd_update(); |
| |
| rc = CheckFirmwareFile(filename, FlashInfo.size, is_romless); |
| rb->lcd_puts(0, 0, "checked:"); |
| switch (rc) |
| { |
| case eOK: |
| rb->lcd_puts(0, 1, "File OK."); |
| break; |
| case eFileNotFound: |
| rb->lcd_puts(0, 1, "File not found."); |
| rb->lcd_puts(0, 2, "Put this in root:"); |
| rb->lcd_puts_scroll(0, 4, filename); |
| break; |
| case eTooBig: |
| rb->lcd_puts(0, 1, "File too big,"); |
| rb->lcd_puts(0, 2, "larger than chip."); |
| break; |
| case eTooSmall: |
| rb->lcd_puts(0, 1, "File too small."); |
| rb->lcd_puts(0, 2, "Incomplete?"); |
| break; |
| case eReadErr: |
| rb->lcd_puts(0, 1, "Read error."); |
| break; |
| case eBadContent: |
| rb->lcd_puts(0, 1, "File invalid."); |
| rb->lcd_puts(0, 2, "Sanity check fail."); |
| break; |
| case eCrcErr: |
| rb->lcd_puts(0, 1, "File invalid."); |
| rb->lcd_puts(0, 2, "CRC check failed,"); |
| rb->lcd_puts(0, 3, "checksum mismatch."); |
| break; |
| case eBadPlatform: |
| rb->lcd_puts(0, 1, "Wrong file for"); |
| rb->lcd_puts(0, 2, "this hardware."); |
| break; |
| default: |
| rb->lcd_puts(0, 1, "Check failed."); |
| break; |
| } |
| |
| if (rc == eOK) |
| { |
| rb->lcd_puts(0, 6, KEYNAME2 " to program"); |
| rb->lcd_puts(0, 7, "other key to exit"); |
| } |
| else |
| { /* error occured */ |
| rb->lcd_puts(0, 6, "Any key to exit"); |
| } |
| |
| rb->lcd_update(); |
| |
| button = WaitForButton(); |
| if (button != KEY2 || rc != eOK) |
| { |
| return; |
| } |
| |
| rb->lcd_clear_display(); |
| rb->lcd_puts(0, 0, "Program all Flash?"); |
| rb->lcd_puts(0, 1, "Are you sure?"); |
| rb->lcd_puts(0, 2, "If it goes wrong,"); |
| rb->lcd_puts(0, 3, "it kills your box!"); |
| rb->lcd_puts(0, 4, "See documentation."); |
| |
| rb->lcd_puts(0, 6, KEYNAME3 " to proceed"); |
| rb->lcd_puts(0, 7, "other key to exit"); |
| rb->lcd_update(); |
| |
| button = WaitForButton(); |
| if (button != KEY3) |
| { |
| return; |
| } |
| |
| rb->lcd_clear_display(); |
| rb->lcd_puts(0, 0, "Programming..."); |
| rb->lcd_update(); |
| |
| rc = ProgramFirmwareFile(filename, FlashInfo.size); |
| if (rc) |
| { /* errors */ |
| rb->lcd_clear_display(); |
| rb->lcd_puts(0, 0, "Panic:"); |
| rb->lcd_puts(0, 1, "Programming fail!"); |
| rb->snprintf(buf, sizeof(buf), "%d errors", rc); |
| rb->lcd_puts(0, 2, buf); |
| rb->lcd_update(); |
| button = WaitForButton(); |
| } |
| |
| rb->lcd_clear_display(); |
| rb->lcd_puts(0, 0, "Verifying..."); |
| rb->lcd_update(); |
| |
| rc = VerifyFirmwareFile(filename); |
| |
| rb->lcd_clear_display(); |
| if (rc == 0) |
| { |
| rb->lcd_puts(0, 0, "Verify OK."); |
| } |
| else |
| { |
| rb->lcd_puts(0, 0, "Panic:"); |
| rb->lcd_puts(0, 1, "Verify fail!"); |
| rb->snprintf(buf, sizeof(buf), "%d errors", rc); |
| rb->lcd_puts(0, 2, buf); |
| } |
| rb->lcd_puts(0, 7, "Any key to exit"); |
| rb->lcd_update(); |
| |
| button = WaitForButton(); |
| } |
| |
| #else /* HAVE_LCD_BITMAP */ |
| /* Player implementation */ |
| |
| /* helper for DoUserDialog() */ |
| void ShowFlashInfo(tFlashInfo* pInfo) |
| { |
| char buf[32]; |
| |
| if (!pInfo->manufacturer) |
| { |
| rb->lcd_puts_scroll(0, 0, "Flash: M=? D=?"); |
| rb->lcd_puts_scroll(0, 1, "Impossible to program"); |
| rb->lcd_update(); |
| WaitForButton(); |
| } |
| else |
| { |
| rb->snprintf(buf, sizeof(buf), "Flash: M=%02x D=%02x", |
| pInfo->manufacturer, pInfo->id); |
| rb->lcd_puts_scroll(0, 0, buf); |
| |
| if (pInfo->size) |
| { |
| rb->snprintf(buf, sizeof(buf), "Size: %d KB", pInfo->size / 1024); |
| rb->lcd_puts_scroll(0, 1, buf); |
| rb->lcd_update(); |
| } |
| else |
| { |
| rb->lcd_puts_scroll(0, 1, "Unsupported chip"); |
| rb->lcd_update(); |
| WaitForButton(); |
| } |
| } |
| } |
| |
| |
| void DoUserDialog(char* filename) |
| { |
| tFlashInfo FlashInfo; |
| char buf[32]; |
| char default_filename[32]; |
| int button; |
| int rc; /* generic return code */ |
| ssize_t memleft; |
| tCheckROM result; |
| bool is_romless; |
| |
| /* this can only work if Rockbox runs in DRAM, not flash ROM */ |
| if ((UINT8*)rb >= FB && (UINT8*)rb < FB + 4096*1024) /* 4 MB max */ |
| { /* we're running from flash */ |
| rb->splash(HZ*3, "Not from ROM"); |
| return; /* exit */ |
| } |
| |
| /* test if the user is running the correct plugin for this box */ |
| if (!CheckPlatform(PLATFORM_ID, *(UINT16*)(FB + VERSION_ADR))) |
| { |
| rb->splash(HZ*3, "Wrong version"); |
| return; /* exit */ |
| } |
| |
| /* refuse to work if the power may fail meanwhile */ |
| if (!rb->battery_level_safe()) |
| { |
| rb->splash(HZ*3, "Batt. too low!"); |
| return; /* exit */ |
| } |
| |
| /* check boot ROM */ |
| result = CheckBootROM(); |
| if (result == eUnknown) |
| { /* no support for any other yet */ |
| rb->splash(HZ*3, "Wrong boot ROM"); |
| return; /* exit */ |
| } |
| is_romless = (result == eROMless); |
| |
| /* compose filename if none given */ |
| if (filename == NULL) |
| { |
| rb->snprintf( |
| default_filename, |
| sizeof(default_filename), |
| "/firmware_%s%s.bin", |
| FILE_TYPE, |
| is_romless ? "_norom" : ""); |
| filename = default_filename; |
| } |
| |
| /* "allocate" memory */ |
| sector = rb->plugin_get_buffer((size_t *)&memleft); |
| if (memleft < SEC_SIZE) /* need buffer for a flash sector */ |
| { |
| rb->splash(HZ*3, "Out of memory"); |
| return; /* exit */ |
| } |
| |
| rc = GetFlashInfo(&FlashInfo); |
| ShowFlashInfo(&FlashInfo); |
| |
| if (FlashInfo.size == 0) /* no valid chip */ |
| { |
| return; /* exit */ |
| } |
| |
| rb->lcd_puts_scroll(0, 0, filename); |
| rb->lcd_puts_scroll(0, 1, "[Menu] to check"); |
| rb->lcd_update(); |
| |
| button = WaitForButton(); |
| if (button != BUTTON_MENU) |
| { |
| return; |
| } |
| |
| rb->lcd_clear_display(); |
| rb->lcd_puts(0, 0, "Checking..."); |
| rb->lcd_update(); |
| |
| rc = CheckFirmwareFile(filename, FlashInfo.size, is_romless); |
| rb->lcd_puts(0, 0, "Checked:"); |
| switch (rc) |
| { |
| case eOK: |
| rb->lcd_puts(0, 1, "File OK."); |
| break; |
| case eFileNotFound: |
| rb->lcd_puts_scroll(0, 0, "File not found:"); |
| rb->lcd_puts_scroll(0, 1, filename); |
| break; |
| case eTooBig: |
| rb->lcd_puts_scroll(0, 0, "File too big,"); |
| rb->lcd_puts_scroll(0, 1, "larger than chip."); |
| break; |
| case eTooSmall: |
| rb->lcd_puts_scroll(0, 0, "File too small."); |
| rb->lcd_puts_scroll(0, 1, "Incomplete?"); |
| break; |
| case eReadErr: |
| rb->lcd_puts_scroll(0, 0, "Read error."); |
| break; |
| case eBadContent: |
| rb->lcd_puts_scroll(0, 0, "File invalid."); |
| rb->lcd_puts_scroll(0, 1, "Sanity check failed."); |
| break; |
| case eCrcErr: |
| rb->lcd_puts_scroll(0, 0, "File invalid."); |
| rb->lcd_puts_scroll(0, 1, "CRC check failed."); |
| break; |
| case eBadPlatform: |
| rb->lcd_puts_scroll(0, 0, "Wrong file for"); |
| rb->lcd_puts_scroll(0, 1, "this hardware."); |
| break; |
| default: |
| rb->lcd_puts_scroll(0, 0, "Check failed."); |
| break; |
| } |
| rb->lcd_update(); |
| |
| rb->sleep(HZ*3); |
| |
| if (rc == eOK) |
| { |
| rb->lcd_puts_scroll(0, 0, "[On] to program,"); |
| rb->lcd_puts_scroll(0, 1, "other key to exit."); |
| rb->lcd_update(); |
| } |
| else |
| { /* error occured */ |
| return; |
| } |
| |
| button = WaitForButton(); |
| |
| if (button != BUTTON_ON) |
| { |
| return; |
| } |
| |
| rb->lcd_clear_display(); |
| rb->lcd_puts_scroll(0, 0, "Are you sure?"); |
| rb->lcd_puts_scroll(0, 1, "[+] to proceed."); |
| rb->lcd_update(); |
| |
| button = WaitForButton(); |
| |
| if (button != BUTTON_RIGHT) |
| { |
| return; |
| } |
| |
| rb->lcd_clear_display(); |
| rb->lcd_puts_scroll(0, 0, "Programming..."); |
| rb->lcd_update(); |
| |
| rc = ProgramFirmwareFile(filename, FlashInfo.size); |
| |
| if (rc) |
| { /* errors */ |
| rb->lcd_clear_display(); |
| rb->lcd_puts_scroll(0, 0, "Programming failed!"); |
| rb->snprintf(buf, sizeof(buf), "%d errors", rc); |
| rb->lcd_puts_scroll(0, 1, buf); |
| rb->lcd_update(); |
| WaitForButton(); |
| } |
| |
| rb->lcd_clear_display(); |
| rb->lcd_puts_scroll(0, 0, "Verifying..."); |
| rb->lcd_update(); |
| |
| rc = VerifyFirmwareFile(filename); |
| |
| rb->lcd_clear_display(); |
| |
| if (rc == 0) |
| { |
| rb->lcd_puts_scroll(0, 0, "Verify OK."); |
| } |
| else |
| { |
| rb->snprintf(buf, sizeof(buf), "Verify failed! %d errors", rc); |
| rb->lcd_puts_scroll(0, 0, buf); |
| } |
| |
| rb->lcd_puts_scroll(0, 1, "Press any key to exit."); |
| rb->lcd_update(); |
| WaitForButton(); |
| } |
| |
| #endif /* not HAVE_LCD_BITMAP */ |
| |
| |
| /***************** Plugin Entry Point *****************/ |
| |
| enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter) |
| { |
| int oldmode; |
| |
| rb = api; /* copy to global api pointer */ |
| |
| /* now go ahead and have fun! */ |
| oldmode = rb->system_memory_guard(MEMGUARD_NONE); /*disable memory guard */ |
| DoUserDialog((char*) parameter); |
| rb->system_memory_guard(oldmode); /* re-enable memory guard */ |
| |
| return PLUGIN_OK; |
| } |
| |
| #endif /* ifdef PLATFORM_ID */ |
| #endif /* #ifndef SIMULATOR */ |