Fix 2.2.2RC1 no gvar compile (#5961)
[opentx.git] / radio / src / storage / eeprom_raw.cpp
blobedda1f87d663de50082ae2cdb6587d777437c83c
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include <stdint.h>
22 #include <inttypes.h>
23 #include <string.h>
24 #include "opentx.h"
25 #include "timers.h"
27 #define EEPROM_MARK 0x84697771 /* thanks ;) */
28 #define EEPROM_ZONE_SIZE (8*1024)
29 #define EEPROM_BUFFER_SIZE 256
30 #define EEPROM_FAT_SIZE 128
31 #define EEPROM_MAX_ZONES (EEPROM_SIZE / EEPROM_ZONE_SIZE)
32 #define EEPROM_MAX_FILES (EEPROM_MAX_ZONES - 1)
33 #define FIRST_FILE_AVAILABLE (1+MAX_MODELS)
35 PACK(struct EepromHeaderFile
37 uint8_t zoneIndex:7;
38 uint8_t exists:1;
39 });
41 PACK(struct EepromHeader
43 uint32_t mark;
44 uint32_t index;
45 EepromHeaderFile files[EEPROM_MAX_FILES];
46 });
48 PACK(struct EepromFileHeader
50 uint16_t fileIndex;
51 uint16_t size;
52 });
54 EepromHeader eepromHeader __DMA;
55 volatile EepromWriteState eepromWriteState = EEPROM_IDLE;
56 uint8_t eepromWriteZoneIndex = FIRST_FILE_AVAILABLE;
57 uint8_t eepromWriteFileIndex;
58 uint16_t eepromWriteSize;
59 uint8_t * eepromWriteSourceAddr;
60 uint32_t eepromWriteDestinationAddr;
61 uint16_t eepromFatAddr = 0;
62 uint8_t eepromWriteBuffer[EEPROM_BUFFER_SIZE] __DMA;
64 void eepromWaitReadStatus()
66 while (eepromReadStatus() == 0) {
67 SIMU_SLEEP(5/*ms*/);
71 void eepromWaitTransferComplete()
73 while (!eepromIsTransferComplete()) {
74 SIMU_SLEEP(5/*ms*/);
78 void eepromEraseBlock(uint32_t address, bool blocking=true)
80 // TRACE("eepromEraseBlock(%d)", address);
82 eepromBlockErase(address);
84 if (blocking) {
85 eepromWaitTransferComplete();
86 eepromWaitReadStatus();
90 void eepromRead(uint8_t * buffer, size_t address, size_t size)
92 // TRACE("eepromRead(%p, %d, %d)", buffer, address, size);
94 eepromStartRead(buffer, address, size);
95 eepromWaitTransferComplete();
98 void eepromWrite(uint8_t * buffer, size_t address, size_t size, bool blocking=true)
100 // TRACE("eepromWrite(%p, %d, %d)", buffer, address, size);
102 eepromStartWrite(buffer, address, size);
104 if (blocking) {
105 eepromWaitTransferComplete();
106 eepromWaitReadStatus();
110 bool eepromOpen()
112 TRACE("eepromOpen");
114 int32_t bestFatAddr = -1;
115 uint32_t bestFatIndex = 0;
116 eepromFatAddr = 0;
117 while (eepromFatAddr < EEPROM_ZONE_SIZE) {
118 eepromRead((uint8_t *)&eepromHeader, eepromFatAddr, sizeof(eepromHeader.mark) + sizeof(eepromHeader.index));
119 if (eepromHeader.mark == EEPROM_MARK && eepromHeader.index >= bestFatIndex) {
120 bestFatAddr = eepromFatAddr;
121 bestFatIndex = eepromHeader.index;
123 eepromFatAddr += EEPROM_FAT_SIZE;
125 if (bestFatAddr >= 0) {
126 eepromFatAddr = bestFatAddr;
127 eepromRead((uint8_t *)&eepromHeader, eepromFatAddr, sizeof(eepromHeader));
128 return true;
130 else {
131 return false;
135 uint32_t readFile(int index, uint8_t * data, uint32_t size)
137 if (eepromHeader.files[index].exists) {
138 EepromFileHeader header;
139 uint32_t address = eepromHeader.files[index].zoneIndex * EEPROM_ZONE_SIZE;
140 eepromRead((uint8_t *)&header, address, sizeof(header));
141 if (size < header.size) {
142 header.size = size;
144 if (header.size > 0) {
145 eepromRead(data, address + sizeof(header), header.size);
146 size -= header.size;
148 if (size > 0) {
149 memset(data + header.size, 0, size);
151 return header.size;
153 else {
154 return 0;
158 void eepromIncFatAddr()
160 eepromHeader.index += 1;
161 eepromFatAddr += EEPROM_FAT_SIZE;
162 if (eepromFatAddr >= EEPROM_ZONE_SIZE) {
163 eepromFatAddr = 0;
167 void writeFile(int index, uint8_t * data, uint32_t size)
169 uint32_t zoneIndex = eepromHeader.files[eepromWriteZoneIndex].zoneIndex;
170 eepromHeader.files[eepromWriteZoneIndex].exists = 0;
171 eepromHeader.files[eepromWriteZoneIndex].zoneIndex = eepromHeader.files[index].zoneIndex;
172 eepromHeader.files[index].exists = (size > 0);
173 eepromHeader.files[index].zoneIndex = zoneIndex;
174 eepromWriteFileIndex = index;
175 eepromWriteSourceAddr = data;
176 eepromWriteSize = size;
177 eepromWriteDestinationAddr = zoneIndex * EEPROM_ZONE_SIZE;
178 eepromWriteState = EEPROM_START_WRITE;
179 eepromWriteZoneIndex += 1;
180 if (eepromWriteZoneIndex >= EEPROM_MAX_FILES) {
181 eepromWriteZoneIndex = FIRST_FILE_AVAILABLE;
183 eepromIncFatAddr();
186 void eeDeleteModel(uint8_t index)
188 storageCheck(true);
189 memclear(&modelHeaders[index], sizeof(ModelHeader));
190 writeFile(index+1, (uint8_t *)&g_model, 0);
191 eepromWriteWait();
194 bool eeCopyModel(uint8_t dst, uint8_t src)
196 storageCheck(true);
198 uint32_t eepromWriteSourceAddr = eepromHeader.files[src+1].zoneIndex * EEPROM_ZONE_SIZE;
199 uint32_t eepromWriteDestinationAddr = eepromHeader.files[dst+1].zoneIndex * EEPROM_ZONE_SIZE;
201 // erase blocks
202 eepromEraseBlock(eepromWriteDestinationAddr);
203 eepromEraseBlock(eepromWriteDestinationAddr+EEPROM_BLOCK_SIZE);
205 // write model
206 for (int pos=0; pos<EEPROM_ZONE_SIZE; pos+=EEPROM_BUFFER_SIZE) {
207 eepromRead(eepromWriteBuffer, eepromWriteSourceAddr+pos, EEPROM_BUFFER_SIZE);
208 eepromWrite(eepromWriteBuffer, eepromWriteDestinationAddr+pos, EEPROM_BUFFER_SIZE);
211 // write FAT
212 eepromHeader.files[dst+1].exists = 1;
213 eepromIncFatAddr();
214 eepromWriteState = EEPROM_WRITE_NEW_FAT;
215 eepromWriteWait();
217 modelHeaders[dst] = modelHeaders[src];
219 return true;
222 void eeSwapModels(uint8_t id1, uint8_t id2)
224 storageCheck(true);
226 EepromHeaderFile tmp = eepromHeader.files[id1+1];
227 eepromHeader.files[id1+1] = eepromHeader.files[id2+1];
228 eepromHeader.files[id2+1] = tmp;
230 eepromIncFatAddr();
231 eepromWriteState = EEPROM_WRITE_NEW_FAT;
232 eepromWriteWait();
235 ModelHeader tmp = modelHeaders[id1];
236 modelHeaders[id1] = modelHeaders[id2];
237 modelHeaders[id2] = tmp;
241 // For conversions ...
242 uint16_t eeLoadGeneralSettingsData()
244 return readFile(0, (uint8_t *)&g_eeGeneral, sizeof(g_eeGeneral));
247 uint16_t eeLoadModelData(uint8_t index)
249 return readFile(index+1, (uint8_t *)&g_model, sizeof(g_model));
252 void writeGeneralSettings()
254 writeFile(0, (uint8_t *)&g_eeGeneral, sizeof(g_eeGeneral));
257 void writeModel(int index)
259 writeFile(index+1, (uint8_t *)&g_model, sizeof(g_model));
262 bool eeLoadGeneral()
264 eeLoadGeneralSettingsData();
266 if (g_eeGeneral.version != EEPROM_VER) {
267 TRACE("EEPROM version %d instead of %d", g_eeGeneral.version, EEPROM_VER);
268 #if defined(PCBSKY9X)
269 if (!eeConvert()) {
270 return false;
272 #else
273 return false;
274 #endif
276 return true;
279 bool eeModelExists(uint8_t id)
281 return (eepromHeader.files[id+1].exists);
284 void eeLoadModelHeader(uint8_t id, ModelHeader * header)
286 readFile(id+1, (uint8_t *)header, sizeof(ModelHeader));
289 void storageFormat()
291 eepromFatAddr = 0;
292 eepromHeader.mark = EEPROM_MARK;
293 eepromHeader.index = 0;
294 for (int i=0; i<EEPROM_MAX_FILES; i++) {
295 eepromHeader.files[i].exists = 0;
296 eepromHeader.files[i].zoneIndex = i+1;
298 eepromEraseBlock(0);
299 eepromEraseBlock(EEPROM_BLOCK_SIZE);
300 eepromWrite((uint8_t *)&eepromHeader, 0, sizeof(eepromHeader));
303 void eepromWriteWait(EepromWriteState state/* = EEPROM_IDLE*/)
305 while (eepromWriteState != state) {
306 #if defined(STM32)
307 // Waits a little bit for CS transitions
308 CoTickDelay(1/*2ms*/);
309 #endif
310 eepromWriteProcess();
311 #ifdef SIMU
312 sleep(5/*ms*/);
313 #endif
317 void storageCheck(bool immediately)
319 if (immediately) {
320 eepromWriteWait();
323 assert(eepromWriteState == EEPROM_IDLE);
325 if (storageDirtyMsk & EE_GENERAL) {
326 TRACE("eeprom write general");
327 storageDirtyMsk -= EE_GENERAL;
328 writeGeneralSettings();
329 if (immediately)
330 eepromWriteWait();
331 else
332 return;
335 if (storageDirtyMsk & EE_MODEL) {
336 TRACE("eeprom write model");
337 storageDirtyMsk -= EE_MODEL;
338 writeModel(g_eeGeneral.currModel);
339 if (immediately)
340 eepromWriteWait();
344 void eepromWriteProcess()
346 // TRACE("eepromWriteProcess(state=%d)", eepromWriteState);
348 switch (eepromWriteState) {
349 case EEPROM_ERASING_FILE_BLOCK1:
350 case EEPROM_ERASING_FILE_BLOCK2:
351 case EEPROM_WRITING_BUFFER:
352 case EEPROM_ERASING_FAT_BLOCK:
353 case EEPROM_WRITING_NEW_FAT:
354 if (eepromIsTransferComplete()) {
355 eepromWriteState = EepromWriteState(eepromWriteState + 1);
357 break;
359 case EEPROM_ERASING_FILE_BLOCK1_WAIT:
360 case EEPROM_ERASING_FILE_BLOCK2_WAIT:
361 case EEPROM_WRITING_BUFFER_WAIT:
362 case EEPROM_ERASING_FAT_BLOCK_WAIT:
363 case EEPROM_WRITING_NEW_FAT_WAIT:
364 if (eepromReadStatus()) {
365 eepromWriteState = EepromWriteState(eepromWriteState + 1);
367 break;
369 case EEPROM_START_WRITE:
370 eepromWriteState = EEPROM_ERASING_FILE_BLOCK1;
371 eepromEraseBlock(eepromWriteDestinationAddr, false);
372 break;
374 case EEPROM_ERASE_FILE_BLOCK2:
375 eepromWriteState = EEPROM_ERASING_FILE_BLOCK2;
376 eepromEraseBlock(eepromWriteDestinationAddr + EEPROM_BLOCK_SIZE, false);
377 break;
379 case EEPROM_WRITE_BUFFER:
381 EepromFileHeader * header = (EepromFileHeader *)eepromWriteBuffer;
382 header->fileIndex = eepromWriteFileIndex;
383 header->size = eepromWriteSize;
384 uint32_t size = min<uint32_t>(EEPROM_BUFFER_SIZE-sizeof(EepromFileHeader), eepromWriteSize);
385 memcpy(eepromWriteBuffer+sizeof(EepromFileHeader), eepromWriteSourceAddr, size);
386 eepromWriteState = EEPROM_WRITING_BUFFER;
387 eepromWrite(eepromWriteBuffer, eepromWriteDestinationAddr, sizeof(EepromFileHeader)+size, false);
388 eepromWriteSourceAddr += size;
389 eepromWriteDestinationAddr += sizeof(EepromFileHeader)+size;
390 eepromWriteSize -= size;
391 break;
394 case EEPROM_WRITE_NEXT_BUFFER:
396 uint32_t size = min<uint32_t>(EEPROM_BUFFER_SIZE, eepromWriteSize);
397 if (size > 0) {
398 memcpy(eepromWriteBuffer, eepromWriteSourceAddr, size);
399 eepromWriteState = EEPROM_WRITING_BUFFER;
400 eepromWrite(eepromWriteBuffer, eepromWriteDestinationAddr, size, false);
401 eepromWriteSourceAddr += size;
402 eepromWriteDestinationAddr += size;
403 eepromWriteSize -= size;
404 break;
406 else if (eepromFatAddr == 0 || eepromFatAddr == EEPROM_BLOCK_SIZE) {
407 eepromWriteState = EEPROM_ERASING_FAT_BLOCK;
408 eepromEraseBlock(eepromFatAddr, false);
409 break;
412 /* no break */
414 case EEPROM_WRITE_NEW_FAT:
415 eepromWriteState = EEPROM_WRITING_NEW_FAT;
416 eepromWrite((uint8_t *)&eepromHeader, eepromFatAddr, sizeof(eepromHeader), false);
417 break;
419 case EEPROM_END_WRITE:
420 eepromWriteState = EEPROM_IDLE;
421 break;
423 default:
424 break;
428 uint16_t eeModelSize(uint8_t index)
430 uint16_t result = 0;
432 if (eepromHeader.files[index+1].exists) {
433 uint32_t address = eepromHeader.files[index+1].zoneIndex * EEPROM_ZONE_SIZE;
434 EepromFileHeader header;
435 eepromRead((uint8_t *)&header, address, sizeof(header));
436 result = header.size;
439 return result;
442 #if defined(SDCARD)
443 const pm_char * eeBackupModel(uint8_t i_fileSrc)
445 char * buf = reusableBuffer.modelsel.mainname;
446 FIL archiveFile;
447 UINT written;
449 storageCheck(true);
451 if (!sdMounted()) {
452 return STR_NO_SDCARD;
455 // check and create folder here
456 strcpy(buf, STR_MODELS_PATH);
457 const char * error = sdCheckAndCreateDirectory(buf);
458 if (error) {
459 return error;
462 buf[sizeof(MODELS_PATH)-1] = '/';
463 strcpy(strcat_modelname(&buf[sizeof(MODELS_PATH)], i_fileSrc), STR_MODELS_EXT);
465 FRESULT result = f_open(&archiveFile, buf, FA_CREATE_ALWAYS | FA_WRITE);
466 if (result != FR_OK) {
467 return SDCARD_ERROR(result);
470 #if defined(PCBSKY9X)
471 strcpy(statusLineMsg, PSTR("File "));
472 strcpy(statusLineMsg+5, &buf[sizeof(MODELS_PATH)]);
473 #endif
475 uint16_t size = eeModelSize(i_fileSrc);
477 *(uint32_t*)&buf[0] = OTX_FOURCC;
478 buf[4] = g_eeGeneral.version;
479 buf[5] = 'M';
480 *(uint16_t*)&buf[6] = size;
482 result = f_write(&archiveFile, buf, 8, &written);
483 if (result != FR_OK || written != 8) {
484 f_close(&archiveFile);
485 return SDCARD_ERROR(result);
488 uint32_t address = eepromHeader.files[i_fileSrc+1].zoneIndex * EEPROM_ZONE_SIZE + sizeof(EepromFileHeader);
489 while (size > 0) {
490 uint16_t blockSize = min<uint16_t>(size, EEPROM_BUFFER_SIZE);
491 eepromRead(eepromWriteBuffer, address, blockSize);
492 result = f_write(&archiveFile, eepromWriteBuffer, blockSize, &written);
493 if (result != FR_OK || written != blockSize) {
494 f_close(&archiveFile);
495 return SDCARD_ERROR(result);
497 size -= blockSize;
498 address += blockSize;
501 f_close(&archiveFile);
503 #if defined(PCBSKY9X)
504 showStatusLine();
505 #endif
507 return NULL;
510 const pm_char * eeRestoreModel(uint8_t i_fileDst, char *model_name)
512 char * buf = reusableBuffer.modelsel.mainname;
513 FIL restoreFile;
514 UINT read;
516 storageCheck(true);
518 if (!sdMounted()) {
519 return STR_NO_SDCARD;
522 strcpy(buf, STR_MODELS_PATH);
523 buf[sizeof(MODELS_PATH)-1] = '/';
524 strcpy(&buf[sizeof(MODELS_PATH)], model_name);
525 strcpy(&buf[strlen(buf)], STR_MODELS_EXT);
527 FRESULT result = f_open(&restoreFile, buf, FA_OPEN_EXISTING | FA_READ);
528 if (result != FR_OK) {
529 return SDCARD_ERROR(result);
532 if (f_size(&restoreFile) < 8) {
533 f_close(&restoreFile);
534 return STR_INCOMPATIBLE;
537 result = f_read(&restoreFile, (uint8_t *)buf, 8, &read);
538 if (result != FR_OK || read != 8) {
539 f_close(&restoreFile);
540 return SDCARD_ERROR(result);
543 uint8_t version = (uint8_t)buf[4];
544 if ((*(uint32_t*)&buf[0] != OTX_FOURCC && *(uint32_t*)&buf[0] != O9X_FOURCC) || version < FIRST_CONV_EEPROM_VER || version > EEPROM_VER || buf[5] != 'M') {
545 f_close(&restoreFile);
546 return STR_INCOMPATIBLE;
549 if (eeModelExists(i_fileDst)) {
550 eeDeleteModel(i_fileDst);
553 uint16_t size = min<uint16_t>(sizeof(g_model), *(uint16_t*)&buf[6]);
554 uint32_t address = eepromHeader.files[i_fileDst+1].zoneIndex * EEPROM_ZONE_SIZE;
556 // erase blocks
557 eepromEraseBlock(address);
558 eepromEraseBlock(address+EEPROM_BLOCK_SIZE);
560 // write header
561 EepromFileHeader * header = (EepromFileHeader *)eepromWriteBuffer;
562 header->fileIndex = i_fileDst+1;
563 header->size = size;
565 int offset = 4;
567 // write model
568 do {
569 uint16_t blockSize = min<uint16_t>(size, EEPROM_BUFFER_SIZE-offset);
570 result = f_read(&restoreFile, eepromWriteBuffer+offset, blockSize, &read);
571 if (result != FR_OK || read != blockSize) {
572 f_close(&g_oLogFile);
573 return SDCARD_ERROR(result);
575 eepromWrite(eepromWriteBuffer, address, blockSize+offset);
576 size -= blockSize;
577 address += EEPROM_BUFFER_SIZE;
578 offset = 0;
579 } while (size > 0);
581 // write FAT
582 eepromHeader.files[i_fileDst+1].exists = 1;
583 eepromIncFatAddr();
584 eepromWriteState = EEPROM_WRITE_NEW_FAT;
585 eepromWriteWait();
587 eeLoadModelHeader(i_fileDst, &modelHeaders[i_fileDst]);
589 #if defined(EEPROM_CONVERSIONS)
590 if (version < EEPROM_VER) {
591 ConvertModel(i_fileDst, version);
592 eeLoadModel(g_eeGeneral.currModel);
594 #endif
596 return NULL;
598 #endif