/* * Copyright (C) OpenTX * * Based on code named * th9x - http://code.google.com/p/th9x * er9x - http://code.google.com/p/er9x * gruvin9x - http://code.google.com/p/gruvin9x * * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include "opentx.h" #include "timers.h" #include "conversions/conversions.h" #define EEPROM_MARK 0x84697771 /* thanks ;) */ #define EEPROM_ZONE_SIZE (8*1024) #define EEPROM_BUFFER_SIZE 256 #define EEPROM_FAT_SIZE 128 #define EEPROM_MAX_ZONES (EEPROM_SIZE / EEPROM_ZONE_SIZE) #define EEPROM_MAX_FILES (EEPROM_MAX_ZONES - 1) #define FIRST_FILE_AVAILABLE (1+MAX_MODELS) PACK(struct EepromHeaderFile { uint8_t zoneIndex:7; uint8_t exists:1; }); PACK(struct EepromHeader { uint32_t mark; uint32_t index; EepromHeaderFile files[EEPROM_MAX_FILES]; }); PACK(struct EepromFileHeader { uint16_t fileIndex; uint16_t size; }); EepromHeader eepromHeader __DMA; volatile EepromWriteState eepromWriteState = EEPROM_IDLE; uint8_t eepromWriteZoneIndex = FIRST_FILE_AVAILABLE; uint8_t eepromWriteFileIndex; uint16_t eepromWriteSize; uint8_t * eepromWriteSourceAddr; uint32_t eepromWriteDestinationAddr; uint16_t eepromFatAddr = 0; uint8_t eepromWriteBuffer[EEPROM_BUFFER_SIZE] __DMA; void eepromWaitReadStatus() { while (!eepromReadStatus()) { } } void eepromWaitTransferComplete() { while (!eepromIsTransferComplete()) { } } void eepromEraseBlock(uint32_t address, bool blocking=true) { // TRACE("eepromEraseBlock(%d)", address); eepromBlockErase(address); if (blocking) { eepromWaitTransferComplete(); eepromWaitReadStatus(); } } void eepromRead(uint8_t * buffer, size_t address, size_t size) { // TRACE("eepromRead(%p, %d, %d)", buffer, address, size); eepromStartRead(buffer, address, size); eepromWaitTransferComplete(); } void eepromWrite(uint8_t * buffer, size_t address, size_t size, bool blocking=true) { // TRACE("eepromWrite(%p, %d, %d)", buffer, address, size); eepromStartWrite(buffer, address, size); if (blocking) { eepromWaitTransferComplete(); eepromWaitReadStatus(); } } bool eepromOpen() { TRACE("eepromOpen"); int32_t bestFatAddr = -1; uint32_t bestFatIndex = 0; eepromFatAddr = 0; while (eepromFatAddr < EEPROM_ZONE_SIZE) { eepromRead((uint8_t *)&eepromHeader, eepromFatAddr, sizeof(eepromHeader.mark) + sizeof(eepromHeader.index)); if (eepromHeader.mark == EEPROM_MARK && eepromHeader.index >= bestFatIndex) { bestFatAddr = eepromFatAddr; bestFatIndex = eepromHeader.index; } eepromFatAddr += EEPROM_FAT_SIZE; } if (bestFatAddr >= 0) { eepromFatAddr = bestFatAddr; eepromRead((uint8_t *)&eepromHeader, eepromFatAddr, sizeof(eepromHeader)); return true; } else { return false; } } uint32_t readFile(int index, uint8_t * data, uint32_t size) { if (eepromHeader.files[index].exists) { EepromFileHeader header; uint32_t address = eepromHeader.files[index].zoneIndex * EEPROM_ZONE_SIZE; eepromRead((uint8_t *)&header, address, sizeof(header)); if (size < header.size) { header.size = size; } if (header.size > 0) { eepromRead(data, address + sizeof(header), header.size); size -= header.size; } if (size > 0) { memset(data + header.size, 0, size); } return header.size; } else { return 0; } } void eepromIncFatAddr() { eepromHeader.index += 1; eepromFatAddr += EEPROM_FAT_SIZE; if (eepromFatAddr >= EEPROM_ZONE_SIZE) { eepromFatAddr = 0; } } void writeFile(int index, uint8_t * data, uint32_t size) { uint32_t zoneIndex = eepromHeader.files[eepromWriteZoneIndex].zoneIndex; eepromHeader.files[eepromWriteZoneIndex].exists = 0; eepromHeader.files[eepromWriteZoneIndex].zoneIndex = eepromHeader.files[index].zoneIndex; eepromHeader.files[index].exists = (size > 0); eepromHeader.files[index].zoneIndex = zoneIndex; eepromWriteFileIndex = index; eepromWriteSourceAddr = data; eepromWriteSize = size; eepromWriteDestinationAddr = zoneIndex * EEPROM_ZONE_SIZE; eepromWriteState = EEPROM_START_WRITE; eepromWriteZoneIndex += 1; if (eepromWriteZoneIndex >= EEPROM_MAX_FILES) { eepromWriteZoneIndex = FIRST_FILE_AVAILABLE; } eepromIncFatAddr(); } void eeDeleteModel(uint8_t index) { storageCheck(true); memclear(&modelHeaders[index], sizeof(ModelHeader)); writeFile(index+1, (uint8_t *)&g_model, 0); eepromWriteWait(); } bool eeCopyModel(uint8_t dst, uint8_t src) { storageCheck(true); uint32_t eepromWriteSourceAddr = eepromHeader.files[src+1].zoneIndex * EEPROM_ZONE_SIZE; uint32_t eepromWriteDestinationAddr = eepromHeader.files[dst+1].zoneIndex * EEPROM_ZONE_SIZE; // erase blocks eepromEraseBlock(eepromWriteDestinationAddr); eepromEraseBlock(eepromWriteDestinationAddr+EEPROM_BLOCK_SIZE); // write model for (int pos=0; posfileIndex = eepromWriteFileIndex; header->size = eepromWriteSize; uint32_t size = min(EEPROM_BUFFER_SIZE-sizeof(EepromFileHeader), eepromWriteSize); memcpy(eepromWriteBuffer+sizeof(EepromFileHeader), eepromWriteSourceAddr, size); eepromWriteState = EEPROM_WRITING_BUFFER; eepromWrite(eepromWriteBuffer, eepromWriteDestinationAddr, sizeof(EepromFileHeader)+size, false); eepromWriteSourceAddr += size; eepromWriteDestinationAddr += sizeof(EepromFileHeader)+size; eepromWriteSize -= size; break; } case EEPROM_WRITE_NEXT_BUFFER: { uint32_t size = min(EEPROM_BUFFER_SIZE, eepromWriteSize); if (size > 0) { memcpy(eepromWriteBuffer, eepromWriteSourceAddr, size); eepromWriteState = EEPROM_WRITING_BUFFER; eepromWrite(eepromWriteBuffer, eepromWriteDestinationAddr, size, false); eepromWriteSourceAddr += size; eepromWriteDestinationAddr += size; eepromWriteSize -= size; break; } else if (eepromFatAddr == 0 || eepromFatAddr == EEPROM_BLOCK_SIZE) { eepromWriteState = EEPROM_ERASING_FAT_BLOCK; eepromEraseBlock(eepromFatAddr, false); break; } } /* no break */ case EEPROM_WRITE_NEW_FAT: eepromWriteState = EEPROM_WRITING_NEW_FAT; eepromWrite((uint8_t *)&eepromHeader, eepromFatAddr, sizeof(eepromHeader), false); break; case EEPROM_END_WRITE: eepromWriteState = EEPROM_IDLE; break; default: break; } } uint16_t eeModelSize(uint8_t index) { uint16_t result = 0; if (eepromHeader.files[index+1].exists) { uint32_t address = eepromHeader.files[index+1].zoneIndex * EEPROM_ZONE_SIZE; EepromFileHeader header; eepromRead((uint8_t *)&header, address, sizeof(header)); result = header.size; } return result; } #if defined(SDCARD) const char * eeBackupModel(uint8_t i_fileSrc) { char * buf = reusableBuffer.modelsel.mainname; FIL archiveFile; UINT written; storageCheck(true); if (!sdMounted()) { return STR_NO_SDCARD; } // check and create folder here strcpy(buf, STR_MODELS_PATH); const char * error = sdCheckAndCreateDirectory(buf); if (error) { return error; } buf[sizeof(MODELS_PATH)-1] = '/'; strcpy(strcat_modelname(&buf[sizeof(MODELS_PATH)], i_fileSrc), STR_MODELS_EXT); FRESULT result = f_open(&archiveFile, buf, FA_CREATE_ALWAYS | FA_WRITE); if (result != FR_OK) { return SDCARD_ERROR(result); } #if defined(PCBSKY9X) strcpy(statusLineMsg, "File "); strcpy(statusLineMsg+5, &buf[sizeof(MODELS_PATH)]); #endif uint16_t size = eeModelSize(i_fileSrc); *(uint32_t*)&buf[0] = OTX_FOURCC; buf[4] = g_eeGeneral.version; buf[5] = 'M'; *(uint16_t*)&buf[6] = size; result = f_write(&archiveFile, buf, 8, &written); if (result != FR_OK || written != 8) { f_close(&archiveFile); return SDCARD_ERROR(result); } uint32_t address = eepromHeader.files[i_fileSrc+1].zoneIndex * EEPROM_ZONE_SIZE + sizeof(EepromFileHeader); while (size > 0) { uint16_t blockSize = min(size, EEPROM_BUFFER_SIZE); eepromRead(eepromWriteBuffer, address, blockSize); result = f_write(&archiveFile, eepromWriteBuffer, blockSize, &written); if (result != FR_OK || written != blockSize) { f_close(&archiveFile); return SDCARD_ERROR(result); } size -= blockSize; address += blockSize; } f_close(&archiveFile); #if defined(PCBSKY9X) showStatusLine(); #endif return NULL; } const char * eeRestoreModel(uint8_t i_fileDst, char *model_name) { char * buf = reusableBuffer.modelsel.mainname; FIL restoreFile; UINT read; storageCheck(true); if (!sdMounted()) { return STR_NO_SDCARD; } strcpy(buf, STR_MODELS_PATH); buf[sizeof(MODELS_PATH)-1] = '/'; strcpy(&buf[sizeof(MODELS_PATH)], model_name); strcpy(&buf[strlen(buf)], STR_MODELS_EXT); FRESULT result = f_open(&restoreFile, buf, FA_OPEN_EXISTING | FA_READ); if (result != FR_OK) { return SDCARD_ERROR(result); } if (f_size(&restoreFile) < 8) { f_close(&restoreFile); return STR_INCOMPATIBLE; } result = f_read(&restoreFile, (uint8_t *)buf, 8, &read); if (result != FR_OK || read != 8) { f_close(&restoreFile); return SDCARD_ERROR(result); } uint8_t version = (uint8_t)buf[4]; if (*(uint32_t*)&buf[0] != OTX_FOURCC || version < FIRST_CONV_EEPROM_VER || version > EEPROM_VER || buf[5] != 'M') { f_close(&restoreFile); return STR_INCOMPATIBLE; } if (eeModelExists(i_fileDst)) { eeDeleteModel(i_fileDst); } uint16_t size = min(sizeof(g_model), *(uint16_t*)&buf[6]); uint32_t address = eepromHeader.files[i_fileDst+1].zoneIndex * EEPROM_ZONE_SIZE; // erase blocks eepromEraseBlock(address); eepromEraseBlock(address+EEPROM_BLOCK_SIZE); // write header EepromFileHeader * header = (EepromFileHeader *)eepromWriteBuffer; header->fileIndex = i_fileDst+1; header->size = size; int offset = 4; // write model do { uint16_t blockSize = min(size, EEPROM_BUFFER_SIZE-offset); result = f_read(&restoreFile, eepromWriteBuffer+offset, blockSize, &read); if (result != FR_OK || read != blockSize) { f_close(&g_oLogFile); return SDCARD_ERROR(result); } eepromWrite(eepromWriteBuffer, address, blockSize+offset); size -= blockSize; address += EEPROM_BUFFER_SIZE; offset = 0; } while (size > 0); // write FAT eepromHeader.files[i_fileDst+1].exists = 1; eepromIncFatAddr(); eepromWriteState = EEPROM_WRITE_NEW_FAT; eepromWriteWait(); eeLoadModelHeader(i_fileDst, &modelHeaders[i_fileDst]); #if defined(EEPROM_CONVERSIONS) if (version < EEPROM_VER) { eeConvertModel(i_fileDst, version); eeLoadModel(g_eeGeneral.currModel); } #endif return NULL; } #endif