Fix doc path
[opentx.git] / radio / src / storage / eeprom_rlc.cpp
blob72758d3b2a1154622fa44eff319c21e964bfe624
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 <inttypes.h>
22 #include <string.h>
23 #include "opentx.h"
24 #include "timers.h"
26 uint8_t s_write_err = 0; // error reasons
27 RlcFile theFile; //used for any file operation
28 EeFs eeFs;
30 #if defined(CPUARM)
31 blkid_t freeBlocks = 0;
32 #endif
34 uint8_t s_sync_write = false;
36 static uint8_t EeFsRead(blkid_t blk, uint8_t ofs)
38 uint8_t byte;
39 eepromReadBlock(&byte, (size_t)(blk*BS+ofs+BLOCKS_OFFSET), 1);
40 return byte;
43 static blkid_t EeFsGetLink(blkid_t blk)
45 #if defined(CPUARM)
46 blkid_t ret;
47 eepromReadBlock((uint8_t *)&ret, blk*BS+BLOCKS_OFFSET, sizeof(blkid_t));
48 return ret;
49 #else
50 return EeFsRead(blk, 0);
51 #endif
54 static void EeFsSetLink(blkid_t blk, blkid_t val)
56 static blkid_t s_link; // we write asynchronously, then nothing on the stack!
57 s_link = val;
58 eepromWriteBlock((uint8_t *)&s_link, (blk*BS)+BLOCKS_OFFSET, sizeof(blkid_t));
61 static uint8_t EeFsGetDat(blkid_t blk, uint8_t ofs)
63 return EeFsRead(blk, ofs+sizeof(blkid_t));
66 static void EeFsSetDat(blkid_t blk, uint8_t ofs, uint8_t *buf, uint8_t len)
68 eepromWriteBlock(buf, (blk*BS)+ofs+sizeof(blkid_t)+BLOCKS_OFFSET, len);
71 static void EeFsFlushFreelist()
73 eepromWriteBlock((uint8_t *)&eeFs.freeList, offsetof(EeFs, freeList), sizeof(eeFs.freeList));
76 static void EeFsFlushDirEnt(uint8_t i_fileId)
78 eepromWriteBlock((uint8_t *)&eeFs.files[i_fileId], offsetof(EeFs, files) + sizeof(DirEnt)*i_fileId, sizeof(DirEnt));
81 static void EeFsFlush()
83 eepromWriteBlock((uint8_t *)&eeFs, 0, sizeof(eeFs));
86 uint16_t EeFsGetFree()
88 #if defined(CPUARM)
89 int32_t ret = freeBlocks * (BS-sizeof(blkid_t));
90 #else
91 int16_t ret = 0;
92 blkid_t i = eeFs.freeList;
93 while (i) {
94 ret += BS-sizeof(blkid_t);
95 i = EeFsGetLink(i);
97 #endif
98 ret += eeFs.files[FILE_TMP].size;
99 ret -= eeFs.files[FILE_MODEL(g_eeGeneral.currModel)].size;
100 return (ret > 0 ? ret : 0);
103 /// free one or more blocks
104 static void EeFsFree(blkid_t blk)
106 blkid_t i = blk;
107 blkid_t tmp;
109 #if defined(CPUARM)
110 freeBlocks++;
111 #endif
113 while ((tmp=EeFsGetLink(i))) {
114 i = tmp;
115 #if defined(CPUARM)
116 freeBlocks++;
117 #endif
120 EeFsSetLink(i, eeFs.freeList);
121 eeFs.freeList = blk; //chain in front
122 EeFsFlushFreelist();
125 void eepromCheck()
127 ENABLE_SYNC_WRITE(true);
129 uint8_t *bufp = (uint8_t *)&g_model;
130 memclear(bufp, BLOCKS);
131 blkid_t blk ;
133 #if defined(CPUARM)
134 blkid_t blocksCount;
135 #endif
136 for (uint8_t i=0; i<=MAXFILES; i++) {
137 #if defined(CPUARM)
138 blocksCount = 0;
139 #endif
140 blkid_t blk = (i==MAXFILES ? eeFs.freeList : eeFs.files[i].startBlk);
141 blkid_t lastBlk = 0;
142 while (blk) {
143 if (blk < FIRSTBLK || // bad blk index
144 blk >= BLOCKS || // bad blk indexchan
145 bufp[blk]) // blk double usage
147 if (lastBlk) {
148 EeFsSetLink(lastBlk, 0);
150 else {
151 EeFsFlush();
153 blk = 0; // abort
155 else {
156 #if defined(CPUARM)
157 blocksCount++;
158 #endif
159 bufp[blk] = i+1;
160 lastBlk = blk;
161 blk = EeFsGetLink(blk);
166 #if defined(CPUARM)
167 freeBlocks = blocksCount;
168 #endif
170 for (blk=FIRSTBLK; blk<BLOCKS; blk++) {
171 if (!bufp[blk]) { // unused block
172 #if defined(CPUARM)
173 freeBlocks++;
174 #endif
175 EeFsSetLink(blk, eeFs.freeList);
176 eeFs.freeList = blk; // chain in front
177 EeFsFlushFreelist();
181 ENABLE_SYNC_WRITE(false);
184 void storageFormat()
186 ENABLE_SYNC_WRITE(true);
188 #if defined(SIMU)
189 // write zero to the end of the new EEPROM file to set it's proper size
190 static uint8_t dummy = 0;
191 eepromWriteBlock(&dummy, EEPROM_SIZE-1, 1);
192 #endif
194 memclear(&eeFs, sizeof(eeFs));
195 eeFs.version = EEFS_VERS;
196 eeFs.mySize = sizeof(eeFs);
197 eeFs.freeList = 0;
198 eeFs.bs = BS;
199 for (blkid_t i=FIRSTBLK; i<BLOCKS-1; i++) {
200 EeFsSetLink(i, i+1);
202 EeFsSetLink(BLOCKS-1, 0);
203 eeFs.freeList = FIRSTBLK;
204 #if defined(CPUARM)
205 freeBlocks = BLOCKS;
206 #endif
207 EeFsFlush();
209 ENABLE_SYNC_WRITE(false);
212 bool eepromOpen()
214 eepromReadBlock((uint8_t *)&eeFs, 0, sizeof(eeFs));
216 #if defined(SIMU)
217 if (eeFs.version != EEFS_VERS) {
218 TRACE("bad eeFs.version (%d instead of %d)", eeFs.version, EEFS_VERS);
220 if (eeFs.mySize != sizeof(eeFs)) {
221 TRACE("bad eeFs.mySize (%d instead of %d)", (int)eeFs.mySize, (int)sizeof(eeFs));
223 #endif
225 if (eeFs.version != EEFS_VERS || eeFs.mySize != sizeof(eeFs)) {
226 return false;
229 eepromCheck();
230 return true;
233 bool EFile::exists(uint8_t i_fileId)
235 return eeFs.files[i_fileId].startBlk;
239 * Swap two files in eeprom
241 void EFile::swap(uint8_t i_fileId1, uint8_t i_fileId2)
243 DirEnt tmp = eeFs.files[i_fileId1];
244 eeFs.files[i_fileId1] = eeFs.files[i_fileId2];
245 eeFs.files[i_fileId2] = tmp;
247 ENABLE_SYNC_WRITE(true);
248 EeFsFlushDirEnt(i_fileId1);
249 EeFsFlushDirEnt(i_fileId2);
250 ENABLE_SYNC_WRITE(false);
253 void EFile::rm(uint8_t i_fileId)
255 blkid_t i = eeFs.files[i_fileId].startBlk;
256 memclear(&eeFs.files[i_fileId], sizeof(eeFs.files[i_fileId]));
257 ENABLE_SYNC_WRITE(true);
258 EeFsFlushDirEnt(i_fileId);
259 if (i) EeFsFree(i); //chain in
260 ENABLE_SYNC_WRITE(false);
264 * Open file i_fileId for reading.
265 * Return the file's type
267 void EFile::openRd(uint8_t i_fileId)
269 m_fileId = i_fileId;
270 m_pos = 0;
271 m_currBlk = eeFs.files[m_fileId].startBlk;
272 m_ofs = 0;
273 s_write_err = ERR_NONE; // error reasons */
276 void RlcFile::openRlc(uint8_t i_fileId)
278 EFile::openRd(i_fileId);
279 m_zeroes = 0;
280 m_bRlc = 0;
283 uint8_t EFile::read(uint8_t *buf, uint8_t i_len)
285 uint16_t len = eeFs.files[m_fileId].size - m_pos;
286 if (i_len > len) i_len = len;
288 uint8_t remaining = i_len;
289 while (remaining) {
290 if (!m_currBlk) break;
292 *buf++ = EeFsGetDat(m_currBlk, m_ofs++);
293 if (m_ofs >= BS-sizeof(blkid_t)) {
294 m_ofs = 0;
295 m_currBlk = EeFsGetLink(m_currBlk);
297 remaining--;
300 i_len -= remaining;
301 m_pos += i_len;
302 return i_len;
306 * Read runlength (RLE) compressed bytes into buf.
308 uint16_t RlcFile::readRlc(uint8_t *buf, uint16_t i_len)
310 uint16_t i = 0;
311 for( ; 1; ) {
312 uint8_t ln = min<uint16_t>(m_zeroes, i_len-i);
313 memclear(&buf[i], ln);
314 i += ln;
315 m_zeroes -= ln;
316 if (m_zeroes) break;
318 ln = min<uint16_t>(m_bRlc, i_len-i);
319 uint8_t lr = read(&buf[i], ln);
320 i += lr ;
321 m_bRlc -= lr;
322 if(m_bRlc) break;
324 if (read(&m_bRlc, 1) !=1) break; // read how many bytes to read
326 assert(m_bRlc & 0x7f);
328 if (m_bRlc&0x80) { // if contains high byte
329 m_zeroes =(m_bRlc>>4) & 0x7;
330 m_bRlc = m_bRlc & 0x0f;
332 else if(m_bRlc&0x40) {
333 m_zeroes = m_bRlc & 0x3f;
334 m_bRlc = 0;
337 return i;
340 void RlcFile::write1(uint8_t b)
342 m_write1_byte = b;
343 write(&m_write1_byte, 1);
346 void RlcFile::write(uint8_t *buf, uint8_t i_len)
348 m_write_len = i_len;
349 m_write_buf = buf;
351 do {
352 nextWriteStep();
353 } while (IS_SYNC_WRITE_ENABLE() && m_write_len && !s_write_err);
356 void RlcFile::nextWriteStep()
358 if (!m_currBlk && m_pos==0) {
359 eeFs.files[FILE_TMP].startBlk = m_currBlk = eeFs.freeList;
360 if (m_currBlk) {
361 #if defined(CPUARM)
362 freeBlocks--;
363 #endif
364 eeFs.freeList = EeFsGetLink(m_currBlk);
365 m_write_step |= WRITE_FIRST_LINK;
366 EeFsFlushFreelist();
367 return;
371 if ((m_write_step & 0x0f) == WRITE_FIRST_LINK) {
372 m_write_step -= WRITE_FIRST_LINK;
373 EeFsSetLink(m_currBlk, 0);
374 return;
377 while (m_write_len) {
378 if (!m_currBlk) {
379 s_write_err = ERR_FULL;
380 break;
382 if (m_ofs >= (BS-sizeof(blkid_t))) {
383 m_ofs = 0;
384 blkid_t nextBlk = EeFsGetLink(m_currBlk);
385 if (!nextBlk) {
386 if (!eeFs.freeList) {
387 s_write_err = ERR_FULL;
388 break;
390 m_write_step += WRITE_NEXT_LINK_1;
391 EeFsSetLink(m_currBlk, eeFs.freeList);
392 // TODO not good
393 return;
395 m_currBlk = nextBlk;
397 switch (m_write_step & 0x0f) {
398 case WRITE_NEXT_LINK_1:
399 m_currBlk = eeFs.freeList;
400 #if defined(CPUARM)
401 freeBlocks--;
402 #endif
403 eeFs.freeList = EeFsGetLink(eeFs.freeList);
404 m_write_step += 1;
405 EeFsFlushFreelist();
406 return;
407 case WRITE_NEXT_LINK_2:
408 m_write_step -= WRITE_NEXT_LINK_2;
409 EeFsSetLink(m_currBlk, 0);
410 return;
412 uint8_t tmp = BS-sizeof(blkid_t)-m_ofs; if(tmp>m_write_len) tmp = m_write_len;
413 m_write_buf += tmp;
414 m_write_len -= tmp;
415 m_ofs += tmp;
416 m_pos += tmp;
417 EeFsSetDat(m_currBlk, m_ofs-tmp, m_write_buf-tmp, tmp);
418 return;
421 if (s_write_err == ERR_FULL) {
422 POPUP_WARNING(STR_EEPROMOVERFLOW);
423 m_write_step = 0;
424 m_write_len = 0;
425 m_cur_rlc_len = 0;
427 else if (!IS_SYNC_WRITE_ENABLE()) {
428 nextRlcWriteStep();
432 void RlcFile::create(uint8_t i_fileId, uint8_t typ, uint8_t sync_write)
434 // all write operations will be executed on FILE_TMP
435 openRlc(FILE_TMP); // internal use
436 eeFs.files[FILE_TMP].typ = typ;
437 eeFs.files[FILE_TMP].size = 0;
438 m_fileId = i_fileId;
439 ENABLE_SYNC_WRITE(sync_write);
443 * Copy file src to dst
445 bool RlcFile::copy(uint8_t i_fileDst, uint8_t i_fileSrc)
447 EFile theFile2;
448 theFile2.openRd(i_fileSrc);
450 create(i_fileDst, FILE_TYP_MODEL/*optimization, only model files are copied. should be eeFs.files[i_fileSrc].typ*/, true);
452 uint8_t buf[BS-sizeof(blkid_t)];
453 uint8_t len;
454 while ((len=theFile2.read(buf, sizeof(buf))))
456 write(buf, len);
457 if (write_errno() != 0) {
458 ENABLE_SYNC_WRITE(false);
459 return false;
463 blkid_t fri=0;
464 if (m_currBlk && (fri=EeFsGetLink(m_currBlk)))
465 EeFsSetLink(m_currBlk, 0);
467 if (fri) EeFsFree(fri); //chain in
469 eeFs.files[FILE_TMP].size = m_pos;
470 EFile::swap(m_fileId, FILE_TMP);
472 assert(!m_write_step);
474 // s_sync_write is set to false in swap();
475 return true;
478 #if defined(SDCARD)
479 const pm_char * eeBackupModel(uint8_t i_fileSrc)
481 char * buf = reusableBuffer.modelsel.mainname;
482 UINT written;
484 // we must close the logs as we reuse the same FIL structure
485 logsClose();
487 // check and create folder here
488 strcpy_P(buf, STR_MODELS_PATH);
489 const char * error = sdCheckAndCreateDirectory(buf);
490 if (error) {
491 return error;
494 buf[sizeof(MODELS_PATH)-1] = '/';
495 eeLoadModelName(i_fileSrc, &buf[sizeof(MODELS_PATH)]);
496 buf[sizeof(MODELS_PATH)+sizeof(g_model.header.name)] = '\0';
498 uint8_t i = sizeof(MODELS_PATH)+sizeof(g_model.header.name)-1;
499 uint8_t len = 0;
500 while (i>sizeof(MODELS_PATH)-1) {
501 if (!len && buf[i])
502 len = i+1;
503 if (len) {
504 if (buf[i])
505 buf[i] = idx2char(buf[i]);
506 else
507 buf[i] = '_';
509 i--;
512 if (len == 0) {
513 uint8_t num = i_fileSrc + 1;
514 strcpy_P(&buf[sizeof(MODELS_PATH)], STR_MODEL);
515 buf[sizeof(MODELS_PATH) + PSIZE(TR_MODEL)] = (char)((num / 10) + '0');
516 buf[sizeof(MODELS_PATH) + PSIZE(TR_MODEL) + 1] = (char)((num % 10) + '0');
517 len = sizeof(MODELS_PATH) + PSIZE(TR_MODEL) + 2;
520 #if defined(RTCLOCK)
521 char * tmp = strAppendDate(&buf[len]);
522 len = tmp - buf;
523 #endif
525 strcpy_P(&buf[len], STR_MODELS_EXT);
527 #ifdef SIMU
528 TRACE("SD-card backup filename=%s", buf);
529 #endif
531 FRESULT result = f_open(&g_oLogFile, buf, FA_CREATE_ALWAYS | FA_WRITE);
532 if (result != FR_OK) {
533 return SDCARD_ERROR(result);
536 EFile theFile2;
537 theFile2.openRd(FILE_MODEL(i_fileSrc));
539 *(uint32_t*)&buf[0] = OTX_FOURCC;
540 buf[4] = g_eeGeneral.version;
541 buf[5] = 'M';
542 *(uint16_t*)&buf[6] = eeModelSize(i_fileSrc);
544 result = f_write(&g_oLogFile, buf, 8, &written);
545 if (result != FR_OK || written != 8) {
546 f_close(&g_oLogFile);
547 return SDCARD_ERROR(result);
550 while ((len=theFile2.read((uint8_t *)buf, 15))) {
551 result = f_write(&g_oLogFile, (uint8_t *)buf, len, &written);
552 if (result != FR_OK || written != len) {
553 f_close(&g_oLogFile);
554 return SDCARD_ERROR(result);
558 f_close(&g_oLogFile);
559 return NULL;
562 const pm_char * eeRestoreModel(uint8_t i_fileDst, char *model_name)
564 char * buf = reusableBuffer.modelsel.mainname;
565 UINT read;
567 // we must close the logs as we reuse the same FIL structure
568 logsClose();
570 strcpy_P(buf, STR_MODELS_PATH);
571 buf[sizeof(MODELS_PATH)-1] = '/';
572 strcpy(&buf[sizeof(MODELS_PATH)], model_name);
573 strcpy_P(&buf[strlen(buf)], STR_MODELS_EXT);
575 FRESULT result = f_open(&g_oLogFile, buf, FA_OPEN_EXISTING | FA_READ);
576 if (result != FR_OK) {
577 return SDCARD_ERROR(result);
580 if (f_size(&g_oLogFile) < 8) {
581 f_close(&g_oLogFile);
582 return STR_INCOMPATIBLE;
585 result = f_read(&g_oLogFile, (uint8_t *)buf, 8, &read);
586 if (result != FR_OK || read != 8) {
587 f_close(&g_oLogFile);
588 return SDCARD_ERROR(result);
591 uint8_t version = (uint8_t)buf[4];
592 if ((*(uint32_t*)&buf[0] != OTX_FOURCC && *(uint32_t*)&buf[0] != O9X_FOURCC) || version < FIRST_CONV_EEPROM_VER || version > EEPROM_VER || buf[5] != 'M') {
593 f_close(&g_oLogFile);
594 return STR_INCOMPATIBLE;
597 if (eeModelExists(i_fileDst)) {
598 eeDeleteModel(i_fileDst);
601 theFile.create(FILE_MODEL(i_fileDst), FILE_TYP_MODEL, true);
603 do {
604 result = f_read(&g_oLogFile, (uint8_t *)buf, 15, &read);
605 if (result != FR_OK) {
606 ENABLE_SYNC_WRITE(false);
607 f_close(&g_oLogFile);
608 return SDCARD_ERROR(result);
610 if (read > 0) {
611 theFile.write((uint8_t *)buf, read);
612 if (write_errno() != 0) {
613 ENABLE_SYNC_WRITE(false);
614 f_close(&g_oLogFile);
615 return STR_EEPROMOVERFLOW;
618 } while (read == 15);
620 blkid_t fri=0;
621 if (theFile.m_currBlk && (fri=EeFsGetLink(theFile.m_currBlk)))
622 EeFsSetLink(theFile.m_currBlk, 0);
624 if (fri) EeFsFree(fri); //chain in
626 eeFs.files[FILE_TMP].size = theFile.m_pos;
627 EFile::swap(theFile.m_fileId, FILE_TMP); // s_sync_write is set to false in swap();
629 f_close(&g_oLogFile);
631 #if defined(EEPROM_CONVERSIONS)
632 if (version < EEPROM_VER) {
633 storageCheck(true);
634 ConvertModel(i_fileDst, version);
635 eeLoadModel(g_eeGeneral.currModel);
637 #endif
639 #if defined(CPUARM)
640 eeLoadModelHeader(i_fileDst, &modelHeaders[i_fileDst]);
641 #endif
643 return NULL;
645 #endif
647 void RlcFile::writeRlc(uint8_t i_fileId, uint8_t typ, uint8_t *buf, uint16_t i_len, uint8_t sync_write)
649 create(i_fileId, typ, sync_write);
651 m_write_step = WRITE_START_STEP;
652 m_rlc_buf = buf;
653 m_rlc_len = i_len;
654 m_cur_rlc_len = 0;
655 #if defined (EEPROM_PROGRESS_BAR)
656 m_ratio = (typ == FILE_TYP_MODEL ? 100 : 10);
657 #endif
659 do {
660 nextRlcWriteStep();
661 } while (IS_SYNC_WRITE_ENABLE() && m_write_step && !s_write_err);
664 void RlcFile::nextRlcWriteStep()
666 uint8_t cnt = 1;
667 uint8_t cnt0 = 0;
668 uint16_t i = 0;
670 if (m_cur_rlc_len) {
671 uint8_t tmp1 = m_cur_rlc_len;
672 uint8_t *tmp2 = m_rlc_buf;
673 m_rlc_buf += m_cur_rlc_len;
674 m_cur_rlc_len = 0;
675 write(tmp2, tmp1);
676 return;
679 if (m_rlc_len>0) {
681 bool run0 = (m_rlc_buf[0] == 0);
683 for (i=1; 1; i++) {
684 bool cur0 = (i<m_rlc_len) ? (m_rlc_buf[i] == 0) : false;
685 if (cur0 != run0 || cnt==0x3f || (cnt0 && cnt==0x0f) || i==m_rlc_len) {
686 if (run0) {
687 assert(cnt0==0);
688 if (cnt<8 && i!=m_rlc_len)
689 cnt0 = cnt; //aufbew fuer spaeter
690 else {
691 m_rlc_buf+=cnt;
692 m_rlc_len-=cnt;
693 write1(cnt|0x40);
694 return;
697 else {
698 m_rlc_buf+=cnt0;
699 m_rlc_len-=cnt0+cnt;
700 m_cur_rlc_len=cnt;
701 if(cnt0){
702 write1(0x80 | (cnt0<<4) | cnt);
704 else{
705 write1(cnt);
707 return;
709 cnt=0;
710 if (i==m_rlc_len) break;
711 run0 = cur0;
713 cnt++;
717 switch(m_write_step) {
718 case WRITE_START_STEP: {
719 blkid_t fri = 0;
721 if (m_currBlk && (fri = EeFsGetLink(m_currBlk))) {
722 // TODO reuse EeFsFree!!!
723 blkid_t prev_freeList = eeFs.freeList;
724 eeFs.freeList = fri;
725 #if defined(CPUARM)
726 freeBlocks++;
727 #endif
728 while (EeFsGetLink(fri)) {
729 fri = EeFsGetLink(fri);
730 #if defined(CPUARM)
731 freeBlocks++;
732 #endif
734 m_write_step = WRITE_FREE_UNUSED_BLOCKS_STEP1;
735 EeFsSetLink(fri, prev_freeList);
736 return;
740 case WRITE_FINAL_DIRENT_STEP: {
741 m_currBlk = eeFs.files[FILE_TMP].startBlk;
742 DirEnt & f = eeFs.files[m_fileId];
743 eeFs.files[FILE_TMP].startBlk = f.startBlk;
744 eeFs.files[FILE_TMP].size = f.size;
745 f.startBlk = m_currBlk;
746 f.size = m_pos;
747 f.typ = eeFs.files[FILE_TMP].typ;
748 m_write_step = WRITE_TMP_DIRENT_STEP;
749 EeFsFlushDirEnt(m_fileId);
750 return;
753 case WRITE_TMP_DIRENT_STEP:
754 m_write_step = 0;
755 EeFsFlushDirEnt(FILE_TMP);
756 return;
758 case WRITE_FREE_UNUSED_BLOCKS_STEP1:
759 m_write_step = WRITE_FREE_UNUSED_BLOCKS_STEP2;
760 EeFsSetLink(m_currBlk, 0);
761 return;
763 case WRITE_FREE_UNUSED_BLOCKS_STEP2:
764 m_write_step = WRITE_FINAL_DIRENT_STEP;
765 EeFsFlushFreelist();
766 return;
770 void RlcFile::flush()
772 while (!eepromIsTransferComplete())
773 wdt_reset();
775 ENABLE_SYNC_WRITE(true);
777 while (m_write_len && !s_write_err)
778 nextWriteStep();
780 while (isWriting() && !s_write_err)
781 nextRlcWriteStep();
783 ENABLE_SYNC_WRITE(false);
786 #if defined (EEPROM_PROGRESS_BAR)
787 void RlcFile::drawProgressBar(uint8_t x)
789 if (storageDirtyMsk || isWriting() || eeprom_buffer_size) {
790 uint8_t len = storageDirtyMsk ? 1 : limit((uint8_t)1, (uint8_t)(7 - (m_rlc_len/m_ratio)), (uint8_t)7);
791 lcdDrawFilledRect(x+1, 0, 5, FH, SOLID, ERASE);
792 lcdDrawFilledRect(x+2, 7-len, 3, len);
795 #endif
797 // For conversions ...
798 #if defined(CPUARM)
799 uint16_t eeLoadGeneralSettingsData()
801 memset(&g_eeGeneral, 0, sizeof(g_eeGeneral));
802 theFile.openRlc(FILE_GENERAL);
803 return theFile.readRlc((uint8_t*)&g_eeGeneral, sizeof(g_eeGeneral));
805 #endif
807 uint16_t eeLoadModelData(uint8_t index)
809 #if defined(CPUARM)
810 memset(&g_model, 0, sizeof(g_model));
811 #endif
812 theFile.openRlc(FILE_MODEL(index));
813 return theFile.readRlc((uint8_t*)&g_model, sizeof(g_model));
816 bool eeLoadGeneral()
818 theFile.openRlc(FILE_GENERAL);
819 if (theFile.readRlc((uint8_t*)&g_eeGeneral, 3) == 3 && g_eeGeneral.version == EEPROM_VER) {
820 theFile.openRlc(FILE_GENERAL);
821 if (theFile.readRlc((uint8_t*)&g_eeGeneral, sizeof(g_eeGeneral)) <= sizeof(g_eeGeneral) && g_eeGeneral.variant == EEPROM_VARIANT) {
822 return true;
826 #if defined(PCBX7)
827 if (g_eeGeneral.variant == 0) {
828 TRACE("Pre release EEPROM detected, variant %d instead of %d for X7 radio. Loading anyway", g_eeGeneral.variant, EEPROM_VARIANT);
829 g_eeGeneral.variant = EEPROM_VARIANT;
830 storageDirty(EE_GENERAL);
831 return true;
833 #endif
835 #if defined(PCBTARANIS)
836 if (g_eeGeneral.variant != EEPROM_VARIANT) {
837 TRACE("EEPROM variant %d instead of %d", g_eeGeneral.variant, EEPROM_VARIANT);
838 return false;
840 #endif
841 #if defined(EEPROM_CONVERSIONS)
842 if (g_eeGeneral.version != EEPROM_VER) {
843 TRACE("EEPROM version %d instead of %d", g_eeGeneral.version, EEPROM_VER);
844 if (!eeConvert()) {
845 return false;
848 return true;
849 #else
850 TRACE("EEPROM version %d (%d) instead of %d (%d)", g_eeGeneral.version, g_eeGeneral.variant, EEPROM_VER, EEPROM_VARIANT);
851 return false;
852 #endif
855 void eeLoadModelName(uint8_t id, char *name)
857 memclear(name, sizeof(g_model.header.name));
858 if (id < MAX_MODELS) {
859 theFile.openRlc(FILE_MODEL(id));
860 theFile.readRlc((uint8_t*)name, sizeof(g_model.header.name));
864 bool eeModelExists(uint8_t id)
866 return EFile::exists(FILE_MODEL(id));
869 void storageCheck(bool immediately)
871 if (immediately) {
872 eeFlush();
875 if (storageDirtyMsk & EE_GENERAL) {
876 TRACE("eeprom write general");
877 storageDirtyMsk -= EE_GENERAL;
878 theFile.writeRlc(FILE_GENERAL, FILE_TYP_GENERAL, (uint8_t*)&g_eeGeneral, sizeof(g_eeGeneral), immediately);
879 if (!immediately) return;
882 if (storageDirtyMsk & EE_MODEL) {
883 TRACE("eeprom write model");
884 storageDirtyMsk = 0;
885 theFile.writeRlc(FILE_MODEL(g_eeGeneral.currModel), FILE_TYP_MODEL, (uint8_t*)&g_model, sizeof(g_model), immediately);
889 #if defined(CPUARM)
890 void eeLoadModelHeader(uint8_t id, ModelHeader *header)
892 memclear(header, sizeof(ModelHeader));
893 if (id < MAX_MODELS) {
894 theFile.openRlc(FILE_MODEL(id));
895 theFile.readRlc((uint8_t*)header, sizeof(ModelHeader));
899 bool eeCopyModel(uint8_t dst, uint8_t src)
901 if (theFile.copy(FILE_MODEL(dst), FILE_MODEL(src))) {
902 memcpy(&modelHeaders[dst], &modelHeaders[src], sizeof(ModelHeader));
903 return true;
905 else {
906 return false;
910 void eeSwapModels(uint8_t id1, uint8_t id2)
912 EFile::swap(FILE_MODEL(id1), FILE_MODEL(id2));
914 char tmp[sizeof(g_model.header)];
915 memcpy(tmp, &modelHeaders[id1], sizeof(ModelHeader));
916 memcpy(&modelHeaders[id1], &modelHeaders[id2], sizeof(ModelHeader));
917 memcpy(&modelHeaders[id2], tmp, sizeof(ModelHeader));
920 void eeDeleteModel(uint8_t idx)
922 EFile::rm(FILE_MODEL(idx));
923 memset(&modelHeaders[idx], 0, sizeof(ModelHeader));
925 #endif
927 #if defined(SDCARD)
928 void eepromBackup()
930 char filename[60];
931 uint8_t buffer[1024];
932 FIL file;
934 // reset unexpectedShutdown to prevent warning when user restores EEPROM backup
935 g_eeGeneral.unexpectedShutdown = 0;
936 storageDirty(EE_GENERAL);
937 storageCheck(true);
939 // create the directory if needed...
940 const char * error = sdCheckAndCreateDirectory(EEPROMS_PATH);
941 if (error) {
942 POPUP_WARNING(error);
943 return;
946 // prepare the filename...
947 char * tmp = strAppend(filename, EEPROMS_PATH "/eeprom");
948 #if defined(RTCLOCK)
949 tmp = strAppendDate(tmp, true);
950 #endif
951 strAppend(tmp, EEPROM_EXT);
953 // open the file for writing...
954 f_open(&file, filename, FA_WRITE | FA_CREATE_ALWAYS);
956 for (int i=0; i<EEPROM_SIZE; i+=1024) {
957 UINT count;
958 eepromReadBlock(buffer, i, 1024);
959 f_write(&file, buffer, 1024, &count);
960 drawProgressBar(STR_WRITING, i, EEPROM_SIZE);
961 SIMU_SLEEP(100/*ms*/);
964 f_close(&file);
966 //set back unexpectedShutdown
967 g_eeGeneral.unexpectedShutdown = 1;
968 storageDirty(EE_GENERAL);
969 storageCheck(true);
971 #endif