📄 rockbox_flash.c
字号:
/**************************************************************************** __________ __ ___.* Open \______ \ ____ ____ | | _\_ |__ _______ ___* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \* \/ \/ \/ \/ \/* $Id: rockbox_flash.c,v 1.14 2004/01/18 17:34:12 hohensoh Exp $** Plugin for reprogramming only the second image in Flash ROM.* !!! DON'T MESS WITH THIS CODE UNLESS YOU'RE ABSOLUTELY SHURE WHAT YOU DO !!!** Copyright (C) 2003 J鰎g Hohensohn aka [IDC]Dragon** All files in this archive are subject to the GNU General Public License.* See the file COPYING in the source tree root for full license agreement.** This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY* KIND, either express or implied.*****************************************************************************/#include "plugin.h"#ifndef SIMULATOR /* Only build 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/* hard-coded values */static volatile UINT8* FB = (UINT8*)0x02000000; /* Flash base address */#define SECTORSIZE 4096 /* size of one flash sector */#define ROCKBOX_DEST 0x09000000#define ROCKBOX_EXEC 0x09000200#define DEFAULT_FILENAME "/rockbox.ucl"#define VERS_ADR 0xFE /* position of firmware version value in Flash */#define UCL_HEADER 26 /* size of the header generated by uclpack */typedef struct { UINT32 destination; /* address to copy it to */ UINT32 size; /* how many bytes of payload (to the next header) */ UINT32 execute; /* entry point */ UINT32 flags; /* uncompressed or compressed */ /* end of header, now comes the payload */} tImageHeader;/* result of the CheckFirmwareFile() function */typedef enum{ eOK = 0, eFileNotFound, /* errors from here on */ eTooBig, eTooSmall, eReadErr, eNotUCL, eWrongAlgorithm, eMultiBlocks,} tCheckResult;typedef struct { UINT8 manufacturer; UINT8 id; int size; char name[32];} tFlashInfo;static struct plugin_api* rb; /* here is a global api struct pointer */static UINT8* sector; /* better not place this on the stack... *//***************** 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); /* round down to 512k align, to make sure */ not_manu = pBase[0]; /* read the normal content */ not_id = pBase[1]; /* should be 'A' (0x41) and 'R' (0x52) 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;}/*********** Tool Functions ************//* place a 32 bit value into memory, big endian */void Write32(UINT8* pByte, UINT32 value){ pByte[0] = (UINT8)(value >> 24); pByte[1] = (UINT8)(value >> 16); pByte[2] = (UINT8)(value >> 8); pByte[3] = (UINT8)(value); }/* read a 32 bit value from memory, big endian */UINT32 Read32(UINT8* pByte){ UINT32 value; value = (UINT32)pByte[0] << 24; value |= (UINT32)pByte[1] << 16; value |= (UINT32)pByte[2] << 8; value |= (UINT32)pByte[3]; return value;}/* get the start address of the second image */tImageHeader* GetSecondImage(void){ tImageHeader* pImage1; UINT32 pos = 0; /* default: not found */ UINT32* pFlash = (UINT32*)FB; /* determine the first image position */ pos = pFlash[2] + pFlash[3]; /* position + size of the bootloader = after it */ pos = (pos + 3) & ~3; /* be sure it's 32 bit aligned */ pImage1 = (tImageHeader*)pos; if (pImage1->destination != ROCKBOX_DEST || pImage1->execute != ROCKBOX_EXEC) return 0; /* seems to be no Archos/Rockbox image in here */ if (pImage1->size != 0) { /* success, we have a second image */ pos = (UINT32)pImage1 + sizeof(tImageHeader) + pImage1->size; if (((pos + SECTORSIZE-1) & ~(SECTORSIZE-1)) != pos) { /* not sector-aligned */ pos = 0; /* sanity check failed */ } } return (tImageHeader*)pos;}/*********** Image File Functions ************//* so far, only compressed images in UCL NRV algorithm 2e supported */tCheckResult CheckImageFile(char* filename, int space, tImageHeader* pHeader, UINT8* pos){ int i; int fd; int filesize; /* size info */ int fileread = 0; /* total size as read from the file */ int read; /* how many for this sector */ /* magic file header for compressed files */ static const UINT8 magic[8] = { 0x00,0xe9,0x55,0x43,0x4c,0xff,0x01,0x1a }; UINT8 ucl_header[UCL_HEADER]; fd = rb->open(filename, O_RDONLY); if (fd < 0) return eFileNotFound; filesize = rb->filesize(fd); if (filesize - (int)sizeof(ucl_header) - 8 > space) { rb->close(fd); return eTooBig; } else if (filesize < 40000) /* give it some reasonable lower limit */ { rb->close(fd); return eTooSmall; } /* do some sanity checks */ read = rb->read(fd, ucl_header, sizeof(ucl_header)); fileread += read; if (read != sizeof(ucl_header)) { rb->close(fd); return eReadErr; } /* compare the magic header */ for (i=0; i<8; i++) { if (ucl_header[i] != magic[i]) { rb->close(fd); return eNotUCL; } } pHeader->size = Read32(ucl_header + 22); /* compressed size */ if (pHeader->size != filesize - sizeof(ucl_header) - 8) { rb->close(fd); return eMultiBlocks; } /* fill in the hardcoded defaults of the header */ pHeader->destination = ROCKBOX_DEST; pHeader->execute = ROCKBOX_EXEC; if (Read32(ucl_header + 18) > pHeader->size) /* compare with uncompressed size */ { /* compressed, normal case */ pHeader->flags = 0x00000001; /* flags for UCL compressed */ /* check for supported algorithm */ if (ucl_header[12] != 0x2E) { rb->close(fd); return eWrongAlgorithm; } } else { /* uncompressed, either to be copied or run directly in flash */ UINT32 reset_vector; /* image has to start with reset vector */ pHeader->flags = 0x00000000; /* uncompressed */ read = rb->read(fd, &reset_vector, sizeof(reset_vector)); fileread += read; if (read != sizeof(reset_vector)) { rb->close(fd); return eReadErr; } pHeader->execute = reset_vector; if (reset_vector != ROCKBOX_EXEC) /* nonstandard address? */ /* assume in-place, executing directly in flash */ pHeader->destination = (UINT32)(pos + sizeof(tImageHeader)); } /* check if we can read the whole file */ do { read = rb->read(fd, sector, SECTORSIZE); fileread += read; } while (read == SECTORSIZE); rb->close(fd); if (fileread != filesize) return eReadErr; return eOK;}/* returns the # of failures, 0 on success */unsigned ProgramImageFile(char* filename, UINT8* pos, tImageHeader* pImageHeader, int start, int size){ int i; int fd; int read; /* how many for this sector */ unsigned failures = 0; fd = rb->open(filename, O_RDONLY); if (fd < 0) return false; /* no error checking necessary here, we checked for minimum size already */ rb->lseek(fd, start, SEEK_SET); /* go to start position */ *(tImageHeader*)sector = *pImageHeader; /* copy header into sector buffer */ read = rb->read(fd, sector + sizeof(tImageHeader), SECTORSIZE - sizeof(tImageHeader)); /* payload behind */ size -= read; read += sizeof(tImageHeader); /* to be programmed, but not part of the file */ do { if (!EraseSector(pos)) { /* nothing we can do, let the programming count the errors */ } for (i=0; i<read; i++) { if (!ProgramByte(pos + i, sector[i])) { failures++; } } pos += SECTORSIZE; read = rb->read(fd, sector, (size > SECTORSIZE) ? SECTORSIZE : size); /* payload for next sector */ size -= read; } while (read > 0); rb->close(fd); return failures;}/* returns the # of failures, 0 on success */unsigned VerifyImageFile(char* filename, UINT8* pos, tImageHeader* pImageHeader, int start, int size){ int i; int fd; int read; /* how many for this sector */ unsigned failures = 0; fd = rb->open(filename, O_RDONLY); if (fd < 0) return false; /* no error checking necessary here, we checked for minimum size already */ rb->lseek(fd, start, SEEK_SET); /* go to start position */ *(tImageHeader*)sector = *pImageHeader; /* copy header into sector buffer */ read = rb->read(fd, sector + sizeof(tImageHeader), SECTORSIZE - sizeof(tImageHeader)); /* payload behind */ size -= read;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -